Skip to main content

db

import "github.com/Ask-Atlas/AskAtlas/api/internal/db"

Index

type Course

type Course struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type CourseFavorite

type CourseFavorite struct {
UserID pgtype.UUID `json:"user_id"`
CourseID pgtype.UUID `json:"course_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type CourseFile

type CourseFile struct {
FileID pgtype.UUID `json:"file_id"`
CourseID pgtype.UUID `json:"course_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type CourseLastViewed

type CourseLastViewed struct {
UserID pgtype.UUID `json:"user_id"`
CourseID pgtype.UUID `json:"course_id"`
ViewedAt pgtype.Timestamptz `json:"viewed_at"`
}

type CourseMember

type CourseMember struct {
UserID pgtype.UUID `json:"user_id"`
SectionID pgtype.UUID `json:"section_id"`
Role CourseRole `json:"role"`
JoinedAt pgtype.Timestamptz `json:"joined_at"`
}

type CourseResource

type CourseResource struct {
ResourceID pgtype.UUID `json:"resource_id"`
CourseID pgtype.UUID `json:"course_id"`
AttachedBy pgtype.UUID `json:"attached_by"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type CourseRole

type CourseRole string
const (
CourseRoleStudent CourseRole = "student"
CourseRoleInstructor CourseRole = "instructor"
CourseRoleTa CourseRole = "ta"
)

func (*CourseRole) Scan

func (e *CourseRole) Scan(src interface{}) error

type CourseSection

type CourseSection struct {
ID pgtype.UUID `json:"id"`
CourseID pgtype.UUID `json:"course_id"`
Term string `json:"term"`
SectionCode pgtype.Text `json:"section_code"`
InstructorName pgtype.Text `json:"instructor_name"`
StartDate pgtype.Date `json:"start_date"`
EndDate pgtype.Date `json:"end_date"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type DBTX

type DBTX interface {
Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error)
Query(context.Context, string, ...interface{}) (pgx.Rows, error)
QueryRow(context.Context, string, ...interface{}) pgx.Row
}

type DeleteGuideFileParams

type DeleteGuideFileParams struct {
FileID pgtype.UUID `json:"file_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type DeleteGuideResourceParams

type DeleteGuideResourceParams struct {
ResourceID pgtype.UUID `json:"resource_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type DeleteStudyGuideRecommendationParams

type DeleteStudyGuideRecommendationParams struct {
StudyGuideID pgtype.UUID `json:"study_guide_id"`
RecommendedBy pgtype.UUID `json:"recommended_by"`
}

type DeleteStudyGuideVoteParams

type DeleteStudyGuideVoteParams struct {
UserID pgtype.UUID `json:"user_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type File

type File struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
}

type FileDeletionStatus

type FileDeletionStatus string
const (
FileDeletionStatusPendingDeletion FileDeletionStatus = "pending_deletion"
FileDeletionStatusDeleted FileDeletionStatus = "deleted"
)

func (*FileDeletionStatus) Scan

func (e *FileDeletionStatus) Scan(src interface{}) error

type FileFavorite

type FileFavorite struct {
UserID pgtype.UUID `json:"user_id"`
FileID pgtype.UUID `json:"file_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type FileGrant

type FileGrant struct {
ID pgtype.UUID `json:"id"`
FileID pgtype.UUID `json:"file_id"`
GranteeType GranteeType `json:"grantee_type"`
GranteeID pgtype.UUID `json:"grantee_id"`
Permission Permission `json:"permission"`
GrantedBy pgtype.UUID `json:"granted_by"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type FileLastViewed

type FileLastViewed struct {
UserID pgtype.UUID `json:"user_id"`
FileID pgtype.UUID `json:"file_id"`
ViewedAt pgtype.Timestamptz `json:"viewed_at"`
}

type FileView

type FileView struct {
ID pgtype.UUID `json:"id"`
FileID pgtype.UUID `json:"file_id"`
UserID pgtype.UUID `json:"user_id"`
ViewedAt pgtype.Timestamptz `json:"viewed_at"`
}

type GetCourseRow

type GetCourseRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type GetFileByOwnerParams

type GetFileByOwnerParams struct {
FileID pgtype.UUID `json:"file_id"`
OwnerID pgtype.UUID `json:"owner_id"`
}

type GetFileByOwnerRow

type GetFileByOwnerRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type GetFileForAttachRow

type GetFileForAttachRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Status UploadStatus `json:"status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
}

type GetFileIfViewableParams

type GetFileIfViewableParams struct {
FileID pgtype.UUID `json:"file_id"`
ViewerID pgtype.UUID `json:"viewer_id"`
CourseIds []pgtype.UUID `json:"course_ids"`
StudyGuideIds []pgtype.UUID `json:"study_guide_ids"`
}

type GetGuideResourceAttacherParams

type GetGuideResourceAttacherParams struct {
ResourceID pgtype.UUID `json:"resource_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type GetMembershipParams

type GetMembershipParams struct {
UserID pgtype.UUID `json:"user_id"`
SectionID pgtype.UUID `json:"section_id"`
}

type GetMembershipRow

type GetMembershipRow struct {
Role CourseRole `json:"role"`
JoinedAt pgtype.Timestamptz `json:"joined_at"`
}

type GetQuizByIDForUpdateRow

type GetQuizByIDForUpdateRow struct {
ID pgtype.UUID `json:"id"`
CreatorID pgtype.UUID `json:"creator_id"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
}

type GetQuizDetailRow

type GetQuizDetailRow struct {
ID pgtype.UUID `json:"id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
}

type GetQuizForUpdateWithParentStatusRow

type GetQuizForUpdateWithParentStatusRow struct {
ID pgtype.UUID `json:"id"`
CreatorID pgtype.UUID `json:"creator_id"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
GuideDeletedAt pgtype.Timestamptz `json:"guide_deleted_at"`
}

type GetResourceByCreatorURLParams

type GetResourceByCreatorURLParams struct {
CreatorID pgtype.UUID `json:"creator_id"`
Url string `json:"url"`
}

type GetResourceByCreatorURLRow

type GetResourceByCreatorURLRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Url string `json:"url"`
Type ResourceType `json:"type"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type GetStudyGuideByIDForUpdateRow

type GetStudyGuideByIDForUpdateRow struct {
ID pgtype.UUID `json:"id"`
CreatorID pgtype.UUID `json:"creator_id"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
}

type GetStudyGuideDetailRow

type GetStudyGuideDetailRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Content pgtype.Text `json:"content"`
Tags []string `json:"tags"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CourseID pgtype.UUID `json:"course_id"`
CourseDepartment string `json:"course_department"`
CourseNumber string `json:"course_number"`
CourseTitle string `json:"course_title"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
}

type GetUserVoteForGuideParams

type GetUserVoteForGuideParams struct {
StudyGuideID pgtype.UUID `json:"study_guide_id"`
ViewerID pgtype.UUID `json:"viewer_id"`
}

type GranteeType

type GranteeType string
const (
GranteeTypeUser GranteeType = "user"
GranteeTypeCourse GranteeType = "course"
GranteeTypeStudyGuide GranteeType = "study_guide"
)

func (*GranteeType) Scan

func (e *GranteeType) Scan(src interface{}) error

type GuideFileAttachedParams

type GuideFileAttachedParams struct {
FileID pgtype.UUID `json:"file_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type InsertFileParams

type InsertFileParams struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
}

type InsertGuideFileParams

type InsertGuideFileParams struct {
FileID pgtype.UUID `json:"file_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type InsertGuideResourceParams

type InsertGuideResourceParams struct {
ResourceID pgtype.UUID `json:"resource_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
AttachedBy pgtype.UUID `json:"attached_by"`
}

type InsertQuizAnswerOptionParams

type InsertQuizAnswerOptionParams struct {
QuestionID pgtype.UUID `json:"question_id"`
Text string `json:"text"`
IsCorrect bool `json:"is_correct"`
SortOrder int32 `json:"sort_order"`
}

type InsertQuizParams

type InsertQuizParams struct {
StudyGuideID pgtype.UUID `json:"study_guide_id"`
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
}

type InsertQuizQuestionParams

type InsertQuizQuestionParams struct {
QuizID pgtype.UUID `json:"quiz_id"`
Type QuestionType `json:"type"`
QuestionText string `json:"question_text"`
Hint pgtype.Text `json:"hint"`
FeedbackCorrect pgtype.Text `json:"feedback_correct"`
FeedbackIncorrect pgtype.Text `json:"feedback_incorrect"`
ReferenceAnswer pgtype.Text `json:"reference_answer"`
SortOrder int32 `json:"sort_order"`
}

type InsertQuizRow

type InsertQuizRow struct {
ID pgtype.UUID `json:"id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type InsertStudyGuideParams

type InsertStudyGuideParams struct {
CourseID pgtype.UUID `json:"course_id"`
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Content pgtype.Text `json:"content"`
Tags []string `json:"tags"`
}

type InsertStudyGuideRecommendationParams

type InsertStudyGuideRecommendationParams struct {
RecommendedBy pgtype.UUID `json:"recommended_by"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type InsertStudyGuideRecommendationRow

type InsertStudyGuideRecommendationRow struct {
CreatedAt pgtype.Timestamptz `json:"created_at"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}

type InsertStudyGuideRow

type InsertStudyGuideRow struct {
ID pgtype.UUID `json:"id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type JoinSectionParams

type JoinSectionParams struct {
UserID pgtype.UUID `json:"user_id"`
SectionID pgtype.UUID `json:"section_id"`
}

type LeaveSectionParams

type LeaveSectionParams struct {
UserID pgtype.UUID `json:"user_id"`
SectionID pgtype.UUID `json:"section_id"`
}

type ListCourseSectionsRow

type ListCourseSectionsRow struct {
ID pgtype.UUID `json:"id"`
Term string `json:"term"`
SectionCode pgtype.Text `json:"section_code"`
InstructorName pgtype.Text `json:"instructor_name"`
StartDate pgtype.Date `json:"start_date"`
MemberCount int64 `json:"member_count"`
}

type ListCoursesCreatedAtAscParams

type ListCoursesCreatedAtAscParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesCreatedAtAscRow

type ListCoursesCreatedAtAscRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesCreatedAtDescParams

type ListCoursesCreatedAtDescParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesCreatedAtDescRow

type ListCoursesCreatedAtDescRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesDepartmentAscParams

type ListCoursesDepartmentAscParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorDepartment pgtype.Text `json:"cursor_department"`
CursorNumber pgtype.Text `json:"cursor_number"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesDepartmentAscRow

type ListCoursesDepartmentAscRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesDepartmentDescParams

type ListCoursesDepartmentDescParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorDepartment pgtype.Text `json:"cursor_department"`
CursorNumber pgtype.Text `json:"cursor_number"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesDepartmentDescRow

type ListCoursesDepartmentDescRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesNumberAscParams

type ListCoursesNumberAscParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorNumber pgtype.Text `json:"cursor_number"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesNumberAscRow

type ListCoursesNumberAscRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesNumberDescParams

type ListCoursesNumberDescParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorNumber pgtype.Text `json:"cursor_number"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesNumberDescRow

type ListCoursesNumberDescRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesTitleAscParams

type ListCoursesTitleAscParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorTitle pgtype.Text `json:"cursor_title"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesTitleAscRow

type ListCoursesTitleAscRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListCoursesTitleDescParams

type ListCoursesTitleDescParams struct {
SchoolID pgtype.UUID `json:"school_id"`
Department pgtype.Text `json:"department"`
Q pgtype.Text `json:"q"`
CursorTitle pgtype.Text `json:"cursor_title"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListCoursesTitleDescRow

type ListCoursesTitleDescRow struct {
ID pgtype.UUID `json:"id"`
SchoolID pgtype.UUID `json:"school_id"`
Department string `json:"department"`
Number string `json:"number"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
SID pgtype.UUID `json:"s_id"`
SName string `json:"s_name"`
SAcronym string `json:"s_acronym"`
SCity pgtype.Text `json:"s_city"`
SState pgtype.Text `json:"s_state"`
SCountry pgtype.Text `json:"s_country"`
}

type ListGuideFilesRow

type ListGuideFilesRow struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
}

type ListGuideQuizzesWithQuestionCountRow

type ListGuideQuizzesWithQuestionCountRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
QuestionCount int64 `json:"question_count"`
}

type ListGuideRecommendersRow

type ListGuideRecommendersRow struct {
ID pgtype.UUID `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}

type ListGuideResourcesRow

type ListGuideResourcesRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Url string `json:"url"`
Type ResourceType `json:"type"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type ListMyEnrollmentsParams

type ListMyEnrollmentsParams struct {
UserID pgtype.UUID `json:"user_id"`
Term pgtype.Text `json:"term"`
Role NullCourseRole `json:"role"`
}

type ListMyEnrollmentsRow

type ListMyEnrollmentsRow struct {
SectionID pgtype.UUID `json:"section_id"`
SectionTerm string `json:"section_term"`
SectionSectionCode pgtype.Text `json:"section_section_code"`
SectionInstructorName pgtype.Text `json:"section_instructor_name"`
CourseID pgtype.UUID `json:"course_id"`
CourseDepartment string `json:"course_department"`
CourseNumber string `json:"course_number"`
CourseTitle string `json:"course_title"`
SchoolID pgtype.UUID `json:"school_id"`
SchoolAcronym string `json:"school_acronym"`
MemberRole CourseRole `json:"member_role"`
MemberJoinedAt pgtype.Timestamptz `json:"member_joined_at"`
}

type ListOwnedFilesCreatedAscParams

type ListOwnedFilesCreatedAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesCreatedAscRow

type ListOwnedFilesCreatedAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesCreatedDescParams

type ListOwnedFilesCreatedDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesCreatedDescRow

type ListOwnedFilesCreatedDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesMimeAscParams

type ListOwnedFilesMimeAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorMimeType pgtype.Text `json:"cursor_mime_type"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesMimeAscRow

type ListOwnedFilesMimeAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesMimeDescParams

type ListOwnedFilesMimeDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorMimeType pgtype.Text `json:"cursor_mime_type"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesMimeDescRow

type ListOwnedFilesMimeDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesNameAscParams

type ListOwnedFilesNameAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorNameLower pgtype.Text `json:"cursor_name_lower"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesNameAscRow

type ListOwnedFilesNameAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesNameDescParams

type ListOwnedFilesNameDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorNameLower pgtype.Text `json:"cursor_name_lower"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesNameDescRow

type ListOwnedFilesNameDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesSizeAscParams

type ListOwnedFilesSizeAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorSize pgtype.Int8 `json:"cursor_size"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesSizeAscRow

type ListOwnedFilesSizeAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesSizeDescParams

type ListOwnedFilesSizeDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorSize pgtype.Int8 `json:"cursor_size"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesSizeDescRow

type ListOwnedFilesSizeDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesStatusAscParams

type ListOwnedFilesStatusAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorStatus NullUploadStatus `json:"cursor_status"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesStatusAscRow

type ListOwnedFilesStatusAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesStatusDescParams

type ListOwnedFilesStatusDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorStatus NullUploadStatus `json:"cursor_status"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesStatusDescRow

type ListOwnedFilesStatusDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesUpdatedAscParams

type ListOwnedFilesUpdatedAscParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesUpdatedAscRow

type ListOwnedFilesUpdatedAscRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListOwnedFilesUpdatedDescParams

type ListOwnedFilesUpdatedDescParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
OwnerID pgtype.UUID `json:"owner_id"`
Status NullUploadStatus `json:"status"`
MimeType pgtype.Text `json:"mime_type"`
MinSize pgtype.Int8 `json:"min_size"`
MaxSize pgtype.Int8 `json:"max_size"`
CreatedFrom pgtype.Timestamptz `json:"created_from"`
CreatedTo pgtype.Timestamptz `json:"created_to"`
UpdatedFrom pgtype.Timestamptz `json:"updated_from"`
UpdatedTo pgtype.Timestamptz `json:"updated_to"`
Q pgtype.Text `json:"q"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListOwnedFilesUpdatedDescRow

type ListOwnedFilesUpdatedDescRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
S3Key string `json:"s3_key"`
Name string `json:"name"`
MimeType string `json:"mime_type"`
Size int64 `json:"size"`
Checksum pgtype.Text `json:"checksum"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletionStatus NullFileDeletionStatus `json:"deletion_status"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
S3DeletedAt pgtype.Timestamptz `json:"s3_deleted_at"`
DeletionJobID pgtype.Text `json:"deletion_job_id"`
FavoritedAt pgtype.Timestamptz `json:"favorited_at"`
LastViewedAt pgtype.Timestamptz `json:"last_viewed_at"`
}

type ListQuizQuestionsByQuizRow

type ListQuizQuestionsByQuizRow struct {
ID pgtype.UUID `json:"id"`
Type QuestionType `json:"type"`
QuestionText string `json:"question_text"`
Hint pgtype.Text `json:"hint"`
FeedbackCorrect pgtype.Text `json:"feedback_correct"`
FeedbackIncorrect pgtype.Text `json:"feedback_incorrect"`
ReferenceAnswer pgtype.Text `json:"reference_answer"`
SortOrder int32 `json:"sort_order"`
}

type ListQuizzesByStudyGuideRow

type ListQuizzesByStudyGuideRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
QuestionCount int64 `json:"question_count"`
}

type ListSchoolsParams

type ListSchoolsParams struct {
Q pgtype.Text `json:"q"`
CursorName pgtype.Text `json:"cursor_name"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
}

type ListSectionMembersParams

type ListSectionMembersParams struct {
SectionID pgtype.UUID `json:"section_id"`
Role NullCourseRole `json:"role"`
CursorJoinedAt pgtype.Timestamptz `json:"cursor_joined_at"`
CursorUserID pgtype.UUID `json:"cursor_user_id"`
PageLimit int32 `json:"page_limit"`
}

type ListSectionMembersRow

type ListSectionMembersRow struct {
UserID pgtype.UUID `json:"user_id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Role CourseRole `json:"role"`
JoinedAt pgtype.Timestamptz `json:"joined_at"`
}

type ListStudyGuidesNewestAscParams

type ListStudyGuidesNewestAscParams struct {
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesNewestAscRow

type ListStudyGuidesNewestAscRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesNewestDescParams

type ListStudyGuidesNewestDescParams struct {
CursorCreatedAt pgtype.Timestamptz `json:"cursor_created_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesNewestDescRow

type ListStudyGuidesNewestDescRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesScoreAscParams

type ListStudyGuidesScoreAscParams struct {
CursorVoteScore pgtype.Int8 `json:"cursor_vote_score"`
CursorViewCount pgtype.Int8 `json:"cursor_view_count"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesScoreAscRow

type ListStudyGuidesScoreAscRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesScoreDescParams

type ListStudyGuidesScoreDescParams struct {
CursorVoteScore pgtype.Int8 `json:"cursor_vote_score"`
CursorViewCount pgtype.Int8 `json:"cursor_view_count"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesScoreDescRow

type ListStudyGuidesScoreDescRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesUpdatedAscParams

type ListStudyGuidesUpdatedAscParams struct {
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesUpdatedAscRow

type ListStudyGuidesUpdatedAscRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesUpdatedDescParams

type ListStudyGuidesUpdatedDescParams struct {
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesUpdatedDescRow

type ListStudyGuidesUpdatedDescRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesViewsAscParams

type ListStudyGuidesViewsAscParams struct {
CursorViewCount pgtype.Int8 `json:"cursor_view_count"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesViewsAscRow

type ListStudyGuidesViewsAscRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type ListStudyGuidesViewsDescParams

type ListStudyGuidesViewsDescParams struct {
CursorViewCount pgtype.Int8 `json:"cursor_view_count"`
CursorUpdatedAt pgtype.Timestamptz `json:"cursor_updated_at"`
CursorID pgtype.UUID `json:"cursor_id"`
PageLimit int32 `json:"page_limit"`
CourseID pgtype.UUID `json:"course_id"`
Q pgtype.Text `json:"q"`
Tags []string `json:"tags"`
}

type ListStudyGuidesViewsDescRow

type ListStudyGuidesViewsDescRow struct {
ID pgtype.UUID `json:"id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Tags []string `json:"tags"`
CourseID pgtype.UUID `json:"course_id"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CreatorID pgtype.UUID `json:"creator_id"`
CreatorFirstName string `json:"creator_first_name"`
CreatorLastName string `json:"creator_last_name"`
VoteScore int64 `json:"vote_score"`
IsRecommended bool `json:"is_recommended"`
QuizCount int64 `json:"quiz_count"`
}

type NullCourseRole

type NullCourseRole struct {
CourseRole CourseRole `json:"course_role"`
Valid bool `json:"valid"` // Valid is true if CourseRole is not NULL
}

func (*NullCourseRole) Scan

func (ns *NullCourseRole) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullCourseRole) Value

func (ns NullCourseRole) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullFileDeletionStatus

type NullFileDeletionStatus struct {
FileDeletionStatus FileDeletionStatus `json:"file_deletion_status"`
Valid bool `json:"valid"` // Valid is true if FileDeletionStatus is not NULL
}

func (*NullFileDeletionStatus) Scan

func (ns *NullFileDeletionStatus) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullFileDeletionStatus) Value

func (ns NullFileDeletionStatus) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullGranteeType

type NullGranteeType struct {
GranteeType GranteeType `json:"grantee_type"`
Valid bool `json:"valid"` // Valid is true if GranteeType is not NULL
}

func (*NullGranteeType) Scan

func (ns *NullGranteeType) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullGranteeType) Value

func (ns NullGranteeType) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullPermission

type NullPermission struct {
Permission Permission `json:"permission"`
Valid bool `json:"valid"` // Valid is true if Permission is not NULL
}

func (*NullPermission) Scan

func (ns *NullPermission) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullPermission) Value

func (ns NullPermission) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullQuestionType

type NullQuestionType struct {
QuestionType QuestionType `json:"question_type"`
Valid bool `json:"valid"` // Valid is true if QuestionType is not NULL
}

func (*NullQuestionType) Scan

func (ns *NullQuestionType) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullQuestionType) Value

func (ns NullQuestionType) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullResourceType

type NullResourceType struct {
ResourceType ResourceType `json:"resource_type"`
Valid bool `json:"valid"` // Valid is true if ResourceType is not NULL
}

func (*NullResourceType) Scan

func (ns *NullResourceType) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullResourceType) Value

func (ns NullResourceType) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullUploadStatus

type NullUploadStatus struct {
UploadStatus UploadStatus `json:"upload_status"`
Valid bool `json:"valid"` // Valid is true if UploadStatus is not NULL
}

func (*NullUploadStatus) Scan

func (ns *NullUploadStatus) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullUploadStatus) Value

func (ns NullUploadStatus) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type NullVoteDirection

type NullVoteDirection struct {
VoteDirection VoteDirection `json:"vote_direction"`
Valid bool `json:"valid"` // Valid is true if VoteDirection is not NULL
}

func (*NullVoteDirection) Scan

func (ns *NullVoteDirection) Scan(value interface{}) error

Scan implements the Scanner interface.

func (NullVoteDirection) Value

func (ns NullVoteDirection) Value() (driver.Value, error)

Value implements the driver Valuer interface.

type Permission

type Permission string
const (
PermissionView Permission = "view"
PermissionShare Permission = "share"
PermissionDelete Permission = "delete"
)

func (*Permission) Scan

func (e *Permission) Scan(src interface{}) error

type PracticeAnswer

type PracticeAnswer struct {
ID pgtype.UUID `json:"id"`
SessionID pgtype.UUID `json:"session_id"`
QuestionID pgtype.UUID `json:"question_id"`
UserAnswer pgtype.Text `json:"user_answer"`
IsCorrect pgtype.Bool `json:"is_correct"`
Verified bool `json:"verified"`
AnsweredAt pgtype.Timestamptz `json:"answered_at"`
}

type PracticeSession

type PracticeSession struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
QuizID pgtype.UUID `json:"quiz_id"`
StartedAt pgtype.Timestamptz `json:"started_at"`
CompletedAt pgtype.Timestamptz `json:"completed_at"`
TotalQuestions int32 `json:"total_questions"`
CorrectAnswers int32 `json:"correct_answers"`
}

type PracticeSessionQuestion

type PracticeSessionQuestion struct {
ID pgtype.UUID `json:"id"`
SessionID pgtype.UUID `json:"session_id"`
QuestionID pgtype.UUID `json:"question_id"`
SortOrder int32 `json:"sort_order"`
}

type Querier

type Querier interface {
// Recomputes the guide's vote_score from study_guide_votes. Returned
// as int64 to match the wire shape on CastVoteResponse. Run after
// the upsert in the same logical request so the response reflects the
// post-mutation state.
ComputeGuideVoteScore(ctx context.Context, studyGuideID pgtype.UUID) (int64, error)
// Single-row existence probe used by join/leave to disambiguate the
// "Course not found" 404 from the "Section not found" 404 the spec
// requires (see ASK-132 / ASK-138).
CourseExists(ctx context.Context, id pgtype.UUID) (bool, error)
// Single-row probe used by the list handler to disambiguate "course
// missing" (404) from "course exists but has no guides" (200 empty
// array). Separate from courses.CourseExists only because sqlc-generated
// queriers are per-file; the predicate is identical.
CourseExistsForGuides(ctx context.Context, id pgtype.UUID) (bool, error)
// Removes the (file_id, study_guide_id) join row. Returns rows-
// affected so the service can detect the concurrency-race case
// (0 rows = a parallel detach already removed it, still maps to
// 404 to match the get-then-delete contract). The file row is
// preserved -- it may be attached to other guides + courses, and
// the spec explicitly forbids cascading the file delete from a
// single detach.
DeleteGuideFile(ctx context.Context, arg DeleteGuideFileParams) (int64, error)
// Removes the join row only. The resources row is preserved -- it
// may be attached to other guides + courses, and the spec
// explicitly forbids cascading the resource delete from a single
// detach. Returns rows-affected so the service can detect
// already-detached races (0 rows -> 404) vs success (1 row -> nil).
DeleteGuideResource(ctx context.Context, arg DeleteGuideResourceParams) (int64, error)
// Hard-delete the (viewer, guide) recommendation row. Returns the
// rows-affected count so the service can distinguish "viewer never
// recommended this guide" (0 rows -> 404 'Recommendation not found')
// from a successful delete (1 row -> 204). The guide-existence +
// role gate runs FIRST in the service so 'Study guide not found' /
// 403 win over 'Recommendation not found' when applicable.
DeleteStudyGuideRecommendation(ctx context.Context, arg DeleteStudyGuideRecommendationParams) (int64, error)
// Hard-delete the (viewer, guide) vote row (ASK-141). Returns the
// rows-affected count so the service can distinguish "no existing
// vote" (0 rows -> 404 'Vote not found') from a successful delete
// (1 row -> 204). The guide-existence check happens BEFORE this
// runs in the service so a missing guide doesn't leak through as
// "vote not found".
DeleteStudyGuideVote(ctx context.Context, arg DeleteStudyGuideVoteParams) (int64, error)
GetCourse(ctx context.Context, id pgtype.UUID) (GetCourseRow, error)
// Fetches a file only if it belongs to the given user and has not been soft-deleted.
// Returns sql.ErrNoRows if not found or already in a deletion state.
GetFileByOwner(ctx context.Context, arg GetFileByOwnerParams) (GetFileByOwnerRow, error)
// File-side gate for AttachFile (ASK-121). Returns the file's
// ownership + status fields so the service can choose 403 (not
// owner) vs 404 (missing / not 'complete' / soft-deleted) without
// a second round trip. Filters NOTHING -- the service inspects
// status + deleted_at + deletion_status to decide vs returning
// pre-filtered for "is the row attachable" since the messages
// differ (403 vs 404) and we want to give the right one.
//
// The columns mirror files.GetFileByOwner -- this is intentional;
// the cross-package read keeps the service layer aware of file
// ownership without reaching into the files package.
GetFileForAttach(ctx context.Context, id pgtype.UUID) (GetFileForAttachRow, error)
GetFileIfViewable(ctx context.Context, arg GetFileIfViewableParams) (File, error)
// Lookup for DetachResource (ASK-116). Returns the attached_by user
// on the join row so the service can run the dual-authz check
// (viewer is guide creator OR viewer is attached_by). sql.ErrNoRows
// maps to 'Resource attachment not found' 404 -- the resource may
// exist but isn't attached to THIS guide.
GetGuideResourceAttacher(ctx context.Context, arg GetGuideResourceAttacherParams) (pgtype.UUID, error)
// Single-row membership lookup powering the per-section
// enrolled/not-enrolled probe (ASK-148). Returns sql.ErrNoRows when the
// viewer is not a member; the service translates that into the 200
// {enrolled:false} response, NOT a 404.
GetMembership(ctx context.Context, arg GetMembershipParams) (GetMembershipRow, error)
// Locked SELECT used at the start of DeleteQuiz (ASK-102) and
// UpdateQuiz (ASK-153). SELECT FOR UPDATE prevents two concurrent
// mutators from racing on the same row -- one wins with 204/200,
// the other sees the post-mutation state in its tx snapshot and
// returns 404. Filters NOTHING -- the service inspects deleted_at
// + creator_id to choose 404 vs 403 vs proceed (mirrors
// studyguides.GetStudyGuideByIDForUpdate).
GetQuizByIDForUpdate(ctx context.Context, id pgtype.UUID) (GetQuizByIDForUpdateRow, error)
// Load the quiz row + privacy-floor creator info for the detail
// payload. The study guide is NOT joined back -- the caller already
// knows the study_guide_id (it's in the URL on POST and on the
// quiz row itself). Excludes soft-deleted quizzes (deleted_at IS
// NULL), soft-deleted creators (u.deleted_at IS NULL), and
// soft-deleted parent guides (sg.deleted_at IS NULL) so a hydration
// that races with a parent-cascade soft-delete reports 'not found'
// rather than rendering an orphaned quiz.
GetQuizDetail(ctx context.Context, id pgtype.UUID) (GetQuizDetailRow, error)
// Locked SELECT used by UpdateQuiz (ASK-153). Like
// GetQuizByIDForUpdate but also joins to the parent study guide
// so the service can enforce the "parent guide must be live"
// gate (AC6) inside the same transaction without a second round
// trip. Locks ONLY the quiz row (FOR UPDATE OF q) -- the parent
// guide is read-only at this site and we don't want to hold a
// write lock on it for the duration of the quiz update.
//
// Filters NOTHING -- the service inspects the four fields to
// choose 404 (missing quiz / deleted quiz / deleted guide) vs
// 403 (not creator) vs proceed.
GetQuizForUpdateWithParentStatus(ctx context.Context, id pgtype.UUID) (GetQuizForUpdateWithParentStatusRow, error)
// Lookup pair for UpsertResource above. Returns the resources row
// the viewer owns for this URL; always succeeds because the upsert
// runs immediately before this in the same tx (either the INSERT
// wrote the row or it was already there).
GetResourceByCreatorURL(ctx context.Context, arg GetResourceByCreatorURLParams) (GetResourceByCreatorURLRow, error)
GetSchool(ctx context.Context, id pgtype.UUID) (School, error)
// Locked SELECT used at the start of DeleteStudyGuide. SELECT FOR
// UPDATE prevents concurrent deletes from racing on the same guide
// (one wins with 204, the other sees the row already-deleted in its
// transaction's snapshot and returns 404). Filters NOTHING -- the
// service inspects deleted_at + creator_id to choose 404 vs 403 vs
// proceed.
GetStudyGuideByIDForUpdate(ctx context.Context, id pgtype.UUID) (GetStudyGuideByIDForUpdateRow, error)
// The detail endpoint's main query (ASK-114). Returns the guide's own
// columns + a compact course payload + a compact creator payload
// + two inline aggregates as subqueries:
// * vote_score -- SUM(up/down votes)
// * is_recommended -- EXISTS in study_guide_recommendations
//
// The viewer's own vote (user_vote) ships in a separate query
// (GetUserVoteForGuide) because sqlc does not infer nullable output
// columns from LEFT JOIN / subquery expressions on enum-typed columns
// -- it reads the schema's NOT NULL constraint and types the output
// non-nullable. An extra round trip is cheaper than fighting sqlc's
// type inference; the PRD's "batching as separate queries" guidance
// explicitly allows it.
//
// Soft-delete invariants:
// * sg.deleted_at IS NULL -- excludes deleted guides (→ 404)
// * u.deleted_at IS NULL -- creator must be live (ASK-143 convention)
//
// Privacy floor: no email, no clerk_id. Creator exposes only
// id/first_name/last_name.
GetStudyGuideDetail(ctx context.Context, id pgtype.UUID) (GetStudyGuideDetailRow, error)
GetUserIDByClerkID(ctx context.Context, clerkID string) (pgtype.UUID, error)
// Returns the viewer's own vote on the guide, or sql.ErrNoRows when
// the viewer has not voted. The service maps ErrNoRows to a nil
// user_vote in the response (JSON null, not omitted).
GetUserVoteForGuide(ctx context.Context, arg GetUserVoteForGuideParams) (VoteDirection, error)
// Live-presence probe used by both vote endpoints. Returns TRUE only
// when the guide row exists AND is not soft-deleted. The vote service
// gates on this before the upsert/delete so a missing-or-deleted
// guide returns 404 with a clear message rather than e.g. trampling
// through to the SQL layer and surfacing a generic FK error.
GuideExistsAndLive(ctx context.Context, id pgtype.UUID) (bool, error)
// Quizzes write + read queries (ASK-150 / ASK-136).
//
// The create flow is wrapped in a single InTx in the service layer:
// InsertQuiz -> N x InsertQuizQuestion -> M x InsertQuizAnswerOption.
// A failure at any step rolls everything back, so a partial quiz can
// never be observed by another reader.
//
// The post-insert hydration runs OUTSIDE the transaction (commit
// happens first) using GetQuizDetail + ListQuizQuestionsByQuiz +
// ListQuizAnswerOptionsByQuiz. The two-list fan-out matches the
// studyguides detail pattern -- mapping options back onto questions
// happens in Go because pgx returns flat rowsets and the question
// count is small (<=100 per quiz).
//
// Privacy floor on the creator payload mirrors studyguides: id +
// first_name + last_name only. No email, no clerk_id.
// Live-presence probe for the create-quiz endpoint. Returns TRUE
// only when the guide row exists AND is not soft-deleted. The
// studyguides package has an identical query (GuideExistsAndLive);
// duplicated here so the quizzes service can stay decoupled from
// the studyguides Repository interface (sqlc generates queriers
// per package -- both call the same row but live in different
// generated method tables).
GuideExistsAndLiveForQuizzes(ctx context.Context, id pgtype.UUID) (bool, error)
// Lookup for DetachFile (ASK-124). Returns TRUE when the (file,
// guide) join row exists. Used as the 404 short-circuit before the
// delete fires -- we want a clean 'File attachment not found' rather
// than relying on DeleteGuideFile :execrows which can't distinguish
// 'never existed' from 'concurrent detach happened first'.
GuideFileAttached(ctx context.Context, arg GuideFileAttachedParams) (bool, error)
InsertFile(ctx context.Context, arg InsertFileParams) (File, error)
// Creates the (file_id, study_guide_id) join row. Uses ON CONFLICT
// DO NOTHING + RETURNING so a duplicate attach surfaces as
// sql.ErrNoRows in Go, which the service maps to a 409 'File is
// already attached to this study guide'. Same pattern as
// recommendations + JoinSection.
//
// No attached_by column on this join table (unlike
// study_guide_resources) -- file ownership is determined from
// files.user_id instead, and the dual-authz check on detach
// compares against guide.creator_id + file.user_id.
InsertGuideFile(ctx context.Context, arg InsertGuideFileParams) (pgtype.Timestamptz, error)
// Creates the (resource_id, study_guide_id, attached_by) join row.
// The PK is (resource_id, study_guide_id) so a same-resource-and-guide
// duplicate raises a unique_violation -- but the user-facing 409
// conflict on a duplicate URL is detected EARLIER by
// URLAlreadyAttachedToGuide (which catches across resource rows
// with the same URL but different creators). This INSERT's PK
// failure mode is the narrow concurrency-race: two attachers slip
// through the pre-check between query 1 and query 4.
InsertGuideResource(ctx context.Context, arg InsertGuideResourceParams) error
// Insert a new quiz row. Returns the columns the service needs to
// build the QuizDetailResponse without an extra round trip on the
// write side -- the read-side hydration still happens via
// GetQuizDetail because the creator's first_name + last_name come
// from a join to users (and would inflate this RETURNING clause).
InsertQuiz(ctx context.Context, arg InsertQuizParams) (InsertQuizRow, error)
// Insert one option row. The service has already validated that
// exactly one option per MCQ has is_correct=true; for true-false
// questions the service synthesises two options (`True` + `False`)
// with the matching is_correct flag.
InsertQuizAnswerOption(ctx context.Context, arg InsertQuizAnswerOptionParams) error
// Insert a single question row. reference_answer is only meaningful
// for `freeform` questions; the service passes NULL for the other
// two types. sort_order is required (the service sets a stable
// value -- either the user-supplied integer or the array index).
InsertQuizQuestion(ctx context.Context, arg InsertQuizQuestionParams) (pgtype.UUID, error)
// Study guide list queries (ASK-104).
//
// Every ListStudyGuides* variant uses the same CTE structure so the
// per-row aggregates (vote_score, is_recommended, quiz_count) are
// computed once and can be referenced from the outer WHERE clause
// (e.g. for the score-sorted cursor predicate). The CTE pattern also
// keeps the 8 named variants near-identical apart from ORDER BY + the
// cursor predicate, which makes future maintenance (e.g. adding a new
// sort field) a mechanical edit.
//
// Soft-delete invariants enforced everywhere:
// * sg.deleted_at IS NULL — excludes guides marked for deletion
// * u.deleted_at IS NULL — excludes guides authored by a
// soft-deleted user; matches the
// convention established by ASK-143's
// section roster (a soft-deleted user
// disappears from public surfaces)
// * quizzes.deleted_at IS NULL — quiz_count excludes deleted quizzes
//
// Privacy floor on the creator payload: only id + first_name + last_name
// are selected. No email, no clerk_id -- same rule as
// SectionMemberResponse in ASK-143.
// Insert a new guide and return all the columns the service needs to
// construct the StudyGuideDetail response without an extra round trip.
// The course preflight (in service.go) gates on AssertCourseExists so
// the FK violation is unreachable in normal flow; the FK still acts
// as a backstop if a course is hard-deleted between preflight + insert.
InsertStudyGuide(ctx context.Context, arg InsertStudyGuideParams) (InsertStudyGuideRow, error)
// Inserts the (study_guide_id, recommended_by) row and returns the
// created_at PLUS the recommender's privacy-floor identity
// (first_name + last_name) via a CTE join to users. One round trip
// builds the entire RecommendationResponse payload; without the
// CTE the service would need a second SELECT against users just to
// pull the recommender's name.
//
// The (study_guide_id, recommended_by) PK from the schema makes a
// duplicate insert raise unique_violation (Postgres SQLSTATE 23505),
// which the service catches and maps to apperrors.ErrConflict (409).
InsertStudyGuideRecommendation(ctx context.Context, arg InsertStudyGuideRecommendationParams) (InsertStudyGuideRecommendationRow, error)
// Adds the user to the section as a 'student'. ON CONFLICT DO NOTHING
// keeps duplicate joins atomic (no PK violation surfacing) and concurrency
// safe; the service layer treats an empty result as the 409 "Already a
// member of this section" case.
JoinSection(ctx context.Context, arg JoinSectionParams) (CourseMember, error)
// Hard-deletes the membership row. RETURNING lets the service detect a
// no-op delete (sql.ErrNoRows) and map it to the 404 "Not a member of
// this section" response.
LeaveSection(ctx context.Context, arg LeaveSectionParams) (pgtype.UUID, error)
// Returns sections with a live member_count via LEFT JOIN (so sections
// with zero members still appear). Ordered most-recent term first using
// start_date when present, falling back to section_code. NULLS LAST keeps
// sections without a known start_date at the bottom.
ListCourseSections(ctx context.Context, courseID pgtype.UUID) ([]ListCourseSectionsRow, error)
ListCoursesCreatedAtAsc(ctx context.Context, arg ListCoursesCreatedAtAscParams) ([]ListCoursesCreatedAtAscRow, error)
ListCoursesCreatedAtDesc(ctx context.Context, arg ListCoursesCreatedAtDescParams) ([]ListCoursesCreatedAtDescRow, error)
// All ListCourses* variants share the same WHERE template
// (school_id, department, q filters + sort-specific cursor) and only
// differ in ORDER BY direction and the cursor field shape. This mirrors
// the per-sort-variant pattern in files.sql -- sqlc cannot parameterize
// ORDER BY, so each direction gets its own named query.
//
// The default sort (by department) uses a composite (department, number, id)
// cursor because (department) alone is not unique; the other sort fields
// use a simpler (field, id) cursor since the field is paired with the
// primary key as a tiebreaker.
ListCoursesDepartmentAsc(ctx context.Context, arg ListCoursesDepartmentAscParams) ([]ListCoursesDepartmentAscRow, error)
ListCoursesDepartmentDesc(ctx context.Context, arg ListCoursesDepartmentDescParams) ([]ListCoursesDepartmentDescRow, error)
ListCoursesNumberAsc(ctx context.Context, arg ListCoursesNumberAscParams) ([]ListCoursesNumberAscRow, error)
ListCoursesNumberDesc(ctx context.Context, arg ListCoursesNumberDescParams) ([]ListCoursesNumberDescRow, error)
ListCoursesTitleAsc(ctx context.Context, arg ListCoursesTitleAscParams) ([]ListCoursesTitleAscRow, error)
ListCoursesTitleDesc(ctx context.Context, arg ListCoursesTitleDescParams) ([]ListCoursesTitleDescRow, error)
// Attached files for the guide detail payload. Privacy floor: no
// user_id, no s3_key, no checksum. The file list shows only what a
// viewer needs to see: what's attached, what type, and how big.
//
// Filters f.status = 'complete' so files mid-upload (pending) or
// failed don't surface in the guide detail -- a frontend that tried
// to download such a file would get a broken link. Only successfully
// uploaded files are visible to non-owners; the upload author's own
// file list (via the files endpoints) shows all statuses so they can
// retry or remove.
ListGuideFiles(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideFilesRow, error)
// Non-deleted quizzes for the guide + question_count per quiz. The
// LEFT JOIN ensures quizzes with zero questions still appear with
// question_count = 0.
ListGuideQuizzesWithQuestionCount(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideQuizzesWithQuestionCountRow, error)
// Recommenders list for the guide detail payload. Same privacy floor
// as CreatorSummary -- id + first_name + last_name only. Excludes
// recommenders whose user record is soft-deleted.
ListGuideRecommenders(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideRecommendersRow, error)
// Attached resources for the guide detail payload. No creator info
// in the SELECT list -- the caller doesn't need to know who attached
// the resource.
ListGuideResources(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideResourcesRow, error)
// Returns every section a user is enrolled in with the compact
// course + school payload the dashboard renders. Optional term + role
// filters use sqlc.narg so the WHERE branch short-circuits when
// they're absent. Sort is fixed: most-recent term first (lexicographic
// "Spring 2026" > "Fall 2025" is acceptable for MVP per the spec),
// then department + number for stable in-term ordering.
ListMyEnrollments(ctx context.Context, arg ListMyEnrollmentsParams) ([]ListMyEnrollmentsRow, error)
ListOwnedFilesCreatedAsc(ctx context.Context, arg ListOwnedFilesCreatedAscParams) ([]ListOwnedFilesCreatedAscRow, error)
ListOwnedFilesCreatedDesc(ctx context.Context, arg ListOwnedFilesCreatedDescParams) ([]ListOwnedFilesCreatedDescRow, error)
ListOwnedFilesMimeAsc(ctx context.Context, arg ListOwnedFilesMimeAscParams) ([]ListOwnedFilesMimeAscRow, error)
ListOwnedFilesMimeDesc(ctx context.Context, arg ListOwnedFilesMimeDescParams) ([]ListOwnedFilesMimeDescRow, error)
ListOwnedFilesNameAsc(ctx context.Context, arg ListOwnedFilesNameAscParams) ([]ListOwnedFilesNameAscRow, error)
ListOwnedFilesNameDesc(ctx context.Context, arg ListOwnedFilesNameDescParams) ([]ListOwnedFilesNameDescRow, error)
ListOwnedFilesSizeAsc(ctx context.Context, arg ListOwnedFilesSizeAscParams) ([]ListOwnedFilesSizeAscRow, error)
ListOwnedFilesSizeDesc(ctx context.Context, arg ListOwnedFilesSizeDescParams) ([]ListOwnedFilesSizeDescRow, error)
ListOwnedFilesStatusAsc(ctx context.Context, arg ListOwnedFilesStatusAscParams) ([]ListOwnedFilesStatusAscRow, error)
ListOwnedFilesStatusDesc(ctx context.Context, arg ListOwnedFilesStatusDescParams) ([]ListOwnedFilesStatusDescRow, error)
ListOwnedFilesUpdatedAsc(ctx context.Context, arg ListOwnedFilesUpdatedAscParams) ([]ListOwnedFilesUpdatedAscRow, error)
ListOwnedFilesUpdatedDesc(ctx context.Context, arg ListOwnedFilesUpdatedDescParams) ([]ListOwnedFilesUpdatedDescRow, error)
// All answer options for every question in a quiz, ordered by
// question_id then sort_order then id. The mapper groups by
// question_id in Go to attach options to their parent question.
// The triple-key ordering keeps the option list deterministic.
ListQuizAnswerOptionsByQuiz(ctx context.Context, quizID pgtype.UUID) ([]QuizAnswerOption, error)
// All questions for a quiz, ordered by sort_order then id (the id
// tiebreaker keeps the response deterministic when two questions
// happen to share a sort_order -- the spec doesn't enforce
// uniqueness on sort_order). Returns reference_answer so the
// mapper can emit it as `correct_answer` on freeform questions.
ListQuizQuestionsByQuiz(ctx context.Context, quizID pgtype.UUID) ([]ListQuizQuestionsByQuizRow, error)
// Lists every non-soft-deleted quiz attached to a study guide
// (ASK-136). Each row carries the privacy-floor creator payload
// (id + first_name + last_name only -- mirrors the studyguides
// surface) plus a server-computed question_count via LEFT JOIN +
// COUNT, so a quiz with zero questions still surfaces with a 0
// count rather than being silently dropped by an INNER JOIN.
//
// Soft-delete invariants:
// * q.deleted_at IS NULL -- excludes soft-deleted quizzes
// (AC3: studyguide with mixed live +
// deleted quizzes only returns the live
// ones)
// * u.deleted_at IS NULL -- excludes quizzes whose creator's user
// record was soft-deleted (matches the
// ASK-143 convention used by
// GetStudyGuideDetail)
//
// The parent study guide's deleted_at is checked separately by the
// service via GuideExistsAndLiveForQuizzes BEFORE this query runs --
// a missing or soft-deleted guide returns 404 even when this query
// would have returned an empty array. Keeps the 404 vs 200-empty
// distinction crisp.
//
// Order: created_at DESC with id DESC as the deterministic
// tiebreaker (the spec calls for "newest first"). The id tiebreaker
// prevents a flaky test on a Postgres that happens to insert two
// rows in the same microsecond.
ListQuizzesByStudyGuide(ctx context.Context, studyGuideID pgtype.UUID) ([]ListQuizzesByStudyGuideRow, error)
ListSchools(ctx context.Context, arg ListSchoolsParams) ([]School, error)
// Returns the section roster joined against users for first/last name.
// Privacy floor: SELECT lists ONLY the five fields exposed in the
// SectionMemberResponse schema (user_id, first_name, last_name, role,
// joined_at). DO NOT add email, clerk_id, or any other user column to
// this list -- the endpoint is reachable by any authenticated user.
//
// Soft-deleted users (users.deleted_at IS NOT NULL) are excluded -- the
// codebase's soft-delete convention is enforced by the partial indexes
// idx_users_deleted_at and idx_users_active_email. A user's soft-delete
// is the signal that they want to disappear from the product, so they
// must not surface in a public-by-design roster. The cursor still
// advances past them in the (joined_at, user_id) keyset, so removing
// them mid-iteration just shrinks the page rather than skipping live
// members.
//
// Optional role filter via sqlc.narg short-circuits when absent. Keyset
// pagination on (joined_at, user_id) -- joined_at alone isn't unique
// (multiple users can join in the same second on a busy section), so
// user_id is the tiebreaker that keeps the keyset a strict total order.
ListSectionMembers(ctx context.Context, arg ListSectionMembersParams) ([]ListSectionMembersRow, error)
ListStudyGuidesNewestAsc(ctx context.Context, arg ListStudyGuidesNewestAscParams) ([]ListStudyGuidesNewestAscRow, error)
ListStudyGuidesNewestDesc(ctx context.Context, arg ListStudyGuidesNewestDescParams) ([]ListStudyGuidesNewestDescRow, error)
ListStudyGuidesScoreAsc(ctx context.Context, arg ListStudyGuidesScoreAscParams) ([]ListStudyGuidesScoreAscRow, error)
// Default sort. Multi-column keyset on (vote_score, view_count,
// updated_at, id) -- each column after vote_score is a tiebreaker for
// the previous one; id is the final strict-total-order tiebreaker.
ListStudyGuidesScoreDesc(ctx context.Context, arg ListStudyGuidesScoreDescParams) ([]ListStudyGuidesScoreDescRow, error)
ListStudyGuidesUpdatedAsc(ctx context.Context, arg ListStudyGuidesUpdatedAscParams) ([]ListStudyGuidesUpdatedAscRow, error)
ListStudyGuidesUpdatedDesc(ctx context.Context, arg ListStudyGuidesUpdatedDescParams) ([]ListStudyGuidesUpdatedDescRow, error)
ListStudyGuidesViewsAsc(ctx context.Context, arg ListStudyGuidesViewsAscParams) ([]ListStudyGuidesViewsAscRow, error)
ListStudyGuidesViewsDesc(ctx context.Context, arg ListStudyGuidesViewsDescParams) ([]ListStudyGuidesViewsDescRow, error)
// Called by the cleanup job handler once S3 deletion is confirmed.
MarkFileDeleted(ctx context.Context, fileID pgtype.UUID) error
// Deletes a file grant matching the exact composite key. No-op if the grant
// does not exist (idempotent).
RevokeFileGrant(ctx context.Context, arg RevokeFileGrantParams) error
// Verifies the section exists AND belongs to the supplied course. A
// section UUID that targets a different course is treated as not found
// to avoid leaking the existence of unrelated sections via the URL path.
SectionInCourseExists(ctx context.Context, arg SectionInCourseExistsParams) (bool, error)
// Records the QStash message ID after publishing the async cleanup job.
SetFileDeletionJobID(ctx context.Context, arg SetFileDeletionJobIDParams) error
// Marks a file as pending deletion. Only applies if the file is owned by the caller
// and has not already entered a deletion state (idempotency-safe).
SoftDeleteFile(ctx context.Context, arg SoftDeleteFileParams) (int64, error)
// Set deleted_at = now() on the quiz. The service has already
// verified the row exists, isn't already deleted, and the viewer
// is the creator -- so this is a blind UPDATE. No cascade: practice
// sessions, questions, and answer options are preserved per the
// ASK-102 spec ("preserve historical practice data; the quiz
// simply becomes invisible to list/detail endpoints").
SoftDeleteQuiz(ctx context.Context, id pgtype.UUID) error
// Application-level cascade: soft-delete every non-deleted quiz on
// the guide. WHERE deleted_at IS NULL preserves the deleted_at
// timestamp on quizzes that were already soft-deleted before the
// guide was -- the spec explicitly requires that an already-deleted
// quiz's deleted_at is NOT updated by this cascade.
SoftDeleteQuizzesForGuide(ctx context.Context, studyGuideID pgtype.UUID) error
// Set deleted_at = now() on the guide. The service has already
// verified the row exists, isn't already deleted, and the viewer is
// the creator -- so this is a blind UPDATE. The DeleteStudyGuide
// transaction wraps this + SoftDeleteQuizzesForGuide.
SoftDeleteStudyGuide(ctx context.Context, id pgtype.UUID) error
SoftDeleteUserByClerkID(ctx context.Context, clerkID string) (int64, error)
// Pre-flight conflict check for AttachResource (ASK-111). Returns
// TRUE when ANY resource with this URL is already attached to the
// given guide -- regardless of who created the resource row. Lets
// the service short-circuit to 409 BEFORE the resource upsert, so a
// duplicate attempt doesn't create or touch a resources row only to
// discard it on the join PK violation.
//
// Why "regardless of creator": the join PK is (resource_id,
// study_guide_id), so two distinct resource rows (different creators
// but same URL) could both attach to the same guide without raising
// the join PK constraint. The spec treats that as a duplicate URL
// on the guide -- this query enforces the no-duplicate-URLs-per-guide
// contract at the application layer.
URLAlreadyAttachedToGuide(ctx context.Context, arg URLAlreadyAttachedToGuideParams) (bool, error)
// Renames a file. Only applies if owned by the caller and not in a deletion state.
// Returns sql.ErrNoRows when file is not found, not owned, or in deletion.
UpdateFile(ctx context.Context, arg UpdateFileParams) (UpdateFileRow, error)
UpdateFileStatus(ctx context.Context, arg UpdateFileStatusParams) error
// Partial update for ASK-153. The title column uses the standard
// COALESCE(narg, current) pattern -- nil means "leave alone".
//
// Description uses a CASE expression because COALESCE can't
// distinguish "field absent in the request" from "field
// explicitly null in the request" (both encode as SQL NULL once
// they reach the query layer). The handler resolves the
// tri-state, sets `clear_description` true on an explicit
// `description: null` body, and the service drives both args
// accordingly:
// * clear_description=false -> column unchanged
// * clear_description=true, description=NULL -> column cleared
// * clear_description=true, description="x" -> column set to "x"
//
// The service is responsible for 404 / 403 gating before this
// query runs (via GetQuizForUpdateWithParentStatus). updated_at
// is bumped to now() on every successful call -- the at-least-
// one-field rule is enforced in Go so this query is never
// reached for an empty PATCH.
UpdateQuiz(ctx context.Context, arg UpdateQuizParams) error
// Partial update for ASK-129. Each updatable column uses COALESCE(narg,
// current) so a nil arg from Go means "leave this column alone" and a
// non-nil arg means "replace with the supplied value". The service is
// responsible for:
// * 404 / 403 gating (via GetStudyGuideByIDForUpdate before this).
// * Validating the at-least-one-field rule (an empty body is a 400
// before this query runs).
// * Tag normalization (trim + lowercase + dedupe) -- the array
// written here is the final canonical form.
//
// The service runs the locked SELECT + this UPDATE in a single
// transaction so a concurrent delete can't slip in between. updated_at
// is bumped to now() on every successful call (the UPDATE sees at
// least the updated_at change even when every other narg is NULL,
// which matches the spec's "updated_at reflects the latest" guarantee
// but also means a no-op PATCH still bumps updated_at -- the service's
// empty-body 400 check prevents that case from reaching SQL).
UpdateStudyGuide(ctx context.Context, arg UpdateStudyGuideParams) error
UpsertClerkUser(ctx context.Context, arg UpsertClerkUserParams) (User, error)
// Inserts a new file grant, returning the row. If the grant already exists
// (same file_id, grantee_type, grantee_id, permission), updates granted_by
// and returns the row. Using DO UPDATE SET avoids a race window where
// concurrent inserts could cause both INSERT and fallback SELECT to miss.
UpsertFileGrant(ctx context.Context, arg UpsertFileGrantParams) (FileGrant, error)
// Inserts a new resources row for the (creator_id, url) pair. The
// ON CONFLICT DO NOTHING preserves the existing row's title /
// description / type when the viewer has used this URL before -- a
// silent overwrite would mutate state visible to the resource's
// other attachments (the same row may be attached to other guides
// + courses).
//
// Paired with GetResourceByCreatorURL: the service runs both calls
// in sequence, then uses the SELECT'd row regardless of whether the
// INSERT actually wrote.
UpsertResource(ctx context.Context, arg UpsertResourceParams) error
// Cast or change a vote (ASK-139). Inserts a new (user_id,
// study_guide_id, vote) row when the viewer has not voted, or
// updates the existing row's vote when the direction changes. Same-
// direction re-submits hit the WHERE clause on the DO UPDATE branch
// and become a true no-op (no row touched, no trigger fired,
// updated_at preserved). The (user_id, study_guide_id) PK from the
// schema is what makes ON CONFLICT resolve correctly.
UpsertStudyGuideVote(ctx context.Context, arg UpsertStudyGuideVoteParams) error
// Combined live-presence + role-gate probe for the recommend
// endpoints (ASK-147 + ASK-101). Returns one row when the viewer
// holds instructor or ta role in AT LEAST ONE section of the guide's
// course AND the guide is live (not soft-deleted).
//
// Returns three booleans so the service can distinguish 404 from
// 403 with a single round trip:
// * guide_exists -- guide row present AND deleted_at IS NULL
// * has_role -- viewer is instructor/ta in some section
// of the guide's course (ignored if guide
// doesn't exist)
//
// Combining the two checks into a single query (rather than two
// sequential calls) keeps the recommend hot path at one DB round
// trip for the gate; the actual insert/delete is the second.
//
// NULL-semantics note: when the guide doesn't exist, the inner
// `guide` CTE returns 0 rows, so `(SELECT course_id FROM guide)` is
// NULL, and `cs.course_id = NULL` is always FALSE (not NULL-equal).
// That makes `has_role` correctly false for missing guides without
// needing a separate WHERE EXISTS guard. The service short-circuits
// on !guide_exists before inspecting has_role, so the two booleans
// are independent by contract even though they correlate in this
// edge case.
ViewerCanRecommendForGuide(ctx context.Context, arg ViewerCanRecommendForGuideParams) (ViewerCanRecommendForGuideRow, error)
}

type Queries

type Queries struct {
// contains filtered or unexported fields
}

func New

func New(db DBTX) *Queries

func (*Queries) ComputeGuideVoteScore

func (q *Queries) ComputeGuideVoteScore(ctx context.Context, studyGuideID pgtype.UUID) (int64, error)

Recomputes the guide's vote_score from study_guide_votes. Returned as int64 to match the wire shape on CastVoteResponse. Run after the upsert in the same logical request so the response reflects the post-mutation state.

func (*Queries) CourseExists

func (q *Queries) CourseExists(ctx context.Context, id pgtype.UUID) (bool, error)

Single-row existence probe used by join/leave to disambiguate the "Course not found" 404 from the "Section not found" 404 the spec requires (see ASK-132 / ASK-138).

func (*Queries) CourseExistsForGuides

func (q *Queries) CourseExistsForGuides(ctx context.Context, id pgtype.UUID) (bool, error)

Single-row probe used by the list handler to disambiguate "course missing" (404) from "course exists but has no guides" (200 empty array). Separate from courses.CourseExists only because sqlc-generated queriers are per-file; the predicate is identical.

func (*Queries) DeleteGuideFile

func (q *Queries) DeleteGuideFile(ctx context.Context, arg DeleteGuideFileParams) (int64, error)

Removes the (file_id, study_guide_id) join row. Returns rows- affected so the service can detect the concurrency-race case (0 rows = a parallel detach already removed it, still maps to 404 to match the get-then-delete contract). The file row is preserved -- it may be attached to other guides + courses, and the spec explicitly forbids cascading the file delete from a single detach.

func (*Queries) DeleteGuideResource

func (q *Queries) DeleteGuideResource(ctx context.Context, arg DeleteGuideResourceParams) (int64, error)

Removes the join row only. The resources row is preserved -- it may be attached to other guides + courses, and the spec explicitly forbids cascading the resource delete from a single detach. Returns rows-affected so the service can detect already-detached races (0 rows -> 404) vs success (1 row -> nil).

func (*Queries) DeleteStudyGuideRecommendation

func (q *Queries) DeleteStudyGuideRecommendation(ctx context.Context, arg DeleteStudyGuideRecommendationParams) (int64, error)

Hard-delete the (viewer, guide) recommendation row. Returns the rows-affected count so the service can distinguish "viewer never recommended this guide" (0 rows -> 404 'Recommendation not found') from a successful delete (1 row -> 204). The guide-existence + role gate runs FIRST in the service so 'Study guide not found' / 403 win over 'Recommendation not found' when applicable.

func (*Queries) DeleteStudyGuideVote

func (q *Queries) DeleteStudyGuideVote(ctx context.Context, arg DeleteStudyGuideVoteParams) (int64, error)

Hard-delete the (viewer, guide) vote row (ASK-141). Returns the rows-affected count so the service can distinguish "no existing vote" (0 rows -> 404 'Vote not found') from a successful delete (1 row -> 204). The guide-existence check happens BEFORE this runs in the service so a missing guide doesn't leak through as "vote not found".

func (*Queries) GetCourse

func (q *Queries) GetCourse(ctx context.Context, id pgtype.UUID) (GetCourseRow, error)

func (*Queries) GetFileByOwner

func (q *Queries) GetFileByOwner(ctx context.Context, arg GetFileByOwnerParams) (GetFileByOwnerRow, error)

Fetches a file only if it belongs to the given user and has not been soft-deleted. Returns sql.ErrNoRows if not found or already in a deletion state.

func (*Queries) GetFileForAttach

func (q *Queries) GetFileForAttach(ctx context.Context, id pgtype.UUID) (GetFileForAttachRow, error)

File-side gate for AttachFile (ASK-121). Returns the file's ownership + status fields so the service can choose 403 (not owner) vs 404 (missing / not 'complete' / soft-deleted) without a second round trip. Filters NOTHING -- the service inspects status + deleted_at + deletion_status to decide vs returning pre-filtered for "is the row attachable" since the messages differ (403 vs 404) and we want to give the right one.

The columns mirror files.GetFileByOwner -- this is intentional; the cross-package read keeps the service layer aware of file ownership without reaching into the files package.

func (*Queries) GetFileIfViewable

func (q *Queries) GetFileIfViewable(ctx context.Context, arg GetFileIfViewableParams) (File, error)

func (*Queries) GetGuideResourceAttacher

func (q *Queries) GetGuideResourceAttacher(ctx context.Context, arg GetGuideResourceAttacherParams) (pgtype.UUID, error)

Lookup for DetachResource (ASK-116). Returns the attached_by user on the join row so the service can run the dual-authz check (viewer is guide creator OR viewer is attached_by). sql.ErrNoRows maps to 'Resource attachment not found' 404 -- the resource may exist but isn't attached to THIS guide.

func (*Queries) GetMembership

func (q *Queries) GetMembership(ctx context.Context, arg GetMembershipParams) (GetMembershipRow, error)

Single-row membership lookup powering the per-section enrolled/not-enrolled probe (ASK-148). Returns sql.ErrNoRows when the viewer is not a member; the service translates that into the 200 {enrolled:false} response, NOT a 404.

func (*Queries) GetQuizByIDForUpdate

func (q *Queries) GetQuizByIDForUpdate(ctx context.Context, id pgtype.UUID) (GetQuizByIDForUpdateRow, error)

Locked SELECT used at the start of DeleteQuiz (ASK-102) and UpdateQuiz (ASK-153). SELECT FOR UPDATE prevents two concurrent mutators from racing on the same row -- one wins with 204/200, the other sees the post-mutation state in its tx snapshot and returns 404. Filters NOTHING -- the service inspects deleted_at + creator_id to choose 404 vs 403 vs proceed (mirrors studyguides.GetStudyGuideByIDForUpdate).

func (*Queries) GetQuizDetail

func (q *Queries) GetQuizDetail(ctx context.Context, id pgtype.UUID) (GetQuizDetailRow, error)

Load the quiz row + privacy-floor creator info for the detail payload. The study guide is NOT joined back -- the caller already knows the study_guide_id (it's in the URL on POST and on the quiz row itself). Excludes soft-deleted quizzes (deleted_at IS NULL), soft-deleted creators (u.deleted_at IS NULL), and soft-deleted parent guides (sg.deleted_at IS NULL) so a hydration that races with a parent-cascade soft-delete reports 'not found' rather than rendering an orphaned quiz.

func (*Queries) GetQuizForUpdateWithParentStatus

func (q *Queries) GetQuizForUpdateWithParentStatus(ctx context.Context, id pgtype.UUID) (GetQuizForUpdateWithParentStatusRow, error)

Locked SELECT used by UpdateQuiz (ASK-153). Like GetQuizByIDForUpdate but also joins to the parent study guide so the service can enforce the "parent guide must be live" gate (AC6) inside the same transaction without a second round trip. Locks ONLY the quiz row (FOR UPDATE OF q) -- the parent guide is read-only at this site and we don't want to hold a write lock on it for the duration of the quiz update.

Filters NOTHING -- the service inspects the four fields to choose 404 (missing quiz / deleted quiz / deleted guide) vs 403 (not creator) vs proceed.

func (*Queries) GetResourceByCreatorURL

func (q *Queries) GetResourceByCreatorURL(ctx context.Context, arg GetResourceByCreatorURLParams) (GetResourceByCreatorURLRow, error)

Lookup pair for UpsertResource above. Returns the resources row the viewer owns for this URL; always succeeds because the upsert runs immediately before this in the same tx (either the INSERT wrote the row or it was already there).

func (*Queries) GetSchool

func (q *Queries) GetSchool(ctx context.Context, id pgtype.UUID) (School, error)

func (*Queries) GetStudyGuideByIDForUpdate

func (q *Queries) GetStudyGuideByIDForUpdate(ctx context.Context, id pgtype.UUID) (GetStudyGuideByIDForUpdateRow, error)

Locked SELECT used at the start of DeleteStudyGuide. SELECT FOR UPDATE prevents concurrent deletes from racing on the same guide (one wins with 204, the other sees the row already-deleted in its transaction's snapshot and returns 404). Filters NOTHING -- the service inspects deleted_at + creator_id to choose 404 vs 403 vs proceed.

func (*Queries) GetStudyGuideDetail

func (q *Queries) GetStudyGuideDetail(ctx context.Context, id pgtype.UUID) (GetStudyGuideDetailRow, error)

The detail endpoint's main query (ASK-114). Returns the guide's own columns + a compact course payload + a compact creator payload + two inline aggregates as subqueries:

  • vote_score -- SUM(up/down votes)
  • is_recommended -- EXISTS in study_guide_recommendations

The viewer's own vote (user_vote) ships in a separate query (GetUserVoteForGuide) because sqlc does not infer nullable output columns from LEFT JOIN / subquery expressions on enum-typed columns -- it reads the schema's NOT NULL constraint and types the output non-nullable. An extra round trip is cheaper than fighting sqlc's type inference; the PRD's "batching as separate queries" guidance explicitly allows it.

Soft-delete invariants:

  • sg.deleted_at IS NULL -- excludes deleted guides (→ 404)
  • u.deleted_at IS NULL -- creator must be live (ASK-143 convention)

Privacy floor: no email, no clerk_id. Creator exposes only id/first_name/last_name.

func (*Queries) GetUserIDByClerkID

func (q *Queries) GetUserIDByClerkID(ctx context.Context, clerkID string) (pgtype.UUID, error)

func (*Queries) GetUserVoteForGuide

func (q *Queries) GetUserVoteForGuide(ctx context.Context, arg GetUserVoteForGuideParams) (VoteDirection, error)

Returns the viewer's own vote on the guide, or sql.ErrNoRows when the viewer has not voted. The service maps ErrNoRows to a nil user_vote in the response (JSON null, not omitted).

func (*Queries) GuideExistsAndLive

func (q *Queries) GuideExistsAndLive(ctx context.Context, id pgtype.UUID) (bool, error)

Live-presence probe used by both vote endpoints. Returns TRUE only when the guide row exists AND is not soft-deleted. The vote service gates on this before the upsert/delete so a missing-or-deleted guide returns 404 with a clear message rather than e.g. trampling through to the SQL layer and surfacing a generic FK error.

func (*Queries) GuideExistsAndLiveForQuizzes

func (q *Queries) GuideExistsAndLiveForQuizzes(ctx context.Context, id pgtype.UUID) (bool, error)

Quizzes write + read queries (ASK-150 / ASK-136).

The create flow is wrapped in a single InTx in the service layer: InsertQuiz -> N x InsertQuizQuestion -> M x InsertQuizAnswerOption. A failure at any step rolls everything back, so a partial quiz can never be observed by another reader.

The post-insert hydration runs OUTSIDE the transaction (commit happens first) using GetQuizDetail + ListQuizQuestionsByQuiz + ListQuizAnswerOptionsByQuiz. The two-list fan-out matches the studyguides detail pattern -- mapping options back onto questions happens in Go because pgx returns flat rowsets and the question count is small (<=100 per quiz).

Privacy floor on the creator payload mirrors studyguides: id + first_name + last_name only. No email, no clerk_id. Live-presence probe for the create-quiz endpoint. Returns TRUE only when the guide row exists AND is not soft-deleted. The studyguides package has an identical query (GuideExistsAndLive); duplicated here so the quizzes service can stay decoupled from the studyguides Repository interface (sqlc generates queriers per package -- both call the same row but live in different generated method tables).

func (*Queries) GuideFileAttached

func (q *Queries) GuideFileAttached(ctx context.Context, arg GuideFileAttachedParams) (bool, error)

Lookup for DetachFile (ASK-124). Returns TRUE when the (file, guide) join row exists. Used as the 404 short-circuit before the delete fires -- we want a clean 'File attachment not found' rather than relying on DeleteGuideFile :execrows which can't distinguish 'never existed' from 'concurrent detach happened first'.

func (*Queries) InsertFile

func (q *Queries) InsertFile(ctx context.Context, arg InsertFileParams) (File, error)

func (*Queries) InsertGuideFile

func (q *Queries) InsertGuideFile(ctx context.Context, arg InsertGuideFileParams) (pgtype.Timestamptz, error)

Creates the (file_id, study_guide_id) join row. Uses ON CONFLICT DO NOTHING + RETURNING so a duplicate attach surfaces as sql.ErrNoRows in Go, which the service maps to a 409 'File is already attached to this study guide'. Same pattern as recommendations + JoinSection.

No attached_by column on this join table (unlike study_guide_resources) -- file ownership is determined from files.user_id instead, and the dual-authz check on detach compares against guide.creator_id + file.user_id.

func (*Queries) InsertGuideResource

func (q *Queries) InsertGuideResource(ctx context.Context, arg InsertGuideResourceParams) error

Creates the (resource_id, study_guide_id, attached_by) join row. The PK is (resource_id, study_guide_id) so a same-resource-and-guide duplicate raises a unique_violation -- but the user-facing 409 conflict on a duplicate URL is detected EARLIER by URLAlreadyAttachedToGuide (which catches across resource rows with the same URL but different creators). This INSERT's PK failure mode is the narrow concurrency-race: two attachers slip through the pre-check between query 1 and query 4.

func (*Queries) InsertQuiz

func (q *Queries) InsertQuiz(ctx context.Context, arg InsertQuizParams) (InsertQuizRow, error)

Insert a new quiz row. Returns the columns the service needs to build the QuizDetailResponse without an extra round trip on the write side -- the read-side hydration still happens via GetQuizDetail because the creator's first_name + last_name come from a join to users (and would inflate this RETURNING clause).

func (*Queries) InsertQuizAnswerOption

func (q *Queries) InsertQuizAnswerOption(ctx context.Context, arg InsertQuizAnswerOptionParams) error

Insert one option row. The service has already validated that exactly one option per MCQ has is_correct=true; for true-false questions the service synthesises two options (`True` + `False`) with the matching is_correct flag.

func (*Queries) InsertQuizQuestion

func (q *Queries) InsertQuizQuestion(ctx context.Context, arg InsertQuizQuestionParams) (pgtype.UUID, error)

Insert a single question row. reference_answer is only meaningful for `freeform` questions; the service passes NULL for the other two types. sort_order is required (the service sets a stable value -- either the user-supplied integer or the array index).

func (*Queries) InsertStudyGuide

func (q *Queries) InsertStudyGuide(ctx context.Context, arg InsertStudyGuideParams) (InsertStudyGuideRow, error)

Study guide list queries (ASK-104).

Every ListStudyGuides* variant uses the same CTE structure so the per-row aggregates (vote_score, is_recommended, quiz_count) are computed once and can be referenced from the outer WHERE clause (e.g. for the score-sorted cursor predicate). The CTE pattern also keeps the 8 named variants near-identical apart from ORDER BY + the cursor predicate, which makes future maintenance (e.g. adding a new sort field) a mechanical edit.

Soft-delete invariants enforced everywhere:

  • sg.deleted_at IS NULL — excludes guides marked for deletion
  • u.deleted_at IS NULL — excludes guides authored by a soft-deleted user; matches the convention established by ASK-143's section roster (a soft-deleted user disappears from public surfaces)
  • quizzes.deleted_at IS NULL — quiz_count excludes deleted quizzes

Privacy floor on the creator payload: only id + first_name + last_name are selected. No email, no clerk_id -- same rule as SectionMemberResponse in ASK-143. Insert a new guide and return all the columns the service needs to construct the StudyGuideDetail response without an extra round trip. The course preflight (in service.go) gates on AssertCourseExists so the FK violation is unreachable in normal flow; the FK still acts as a backstop if a course is hard-deleted between preflight + insert.

func (*Queries) InsertStudyGuideRecommendation

func (q *Queries) InsertStudyGuideRecommendation(ctx context.Context, arg InsertStudyGuideRecommendationParams) (InsertStudyGuideRecommendationRow, error)

Inserts the (study_guide_id, recommended_by) row and returns the created_at PLUS the recommender's privacy-floor identity (first_name + last_name) via a CTE join to users. One round trip builds the entire RecommendationResponse payload; without the CTE the service would need a second SELECT against users just to pull the recommender's name.

The (study_guide_id, recommended_by) PK from the schema makes a duplicate insert raise unique_violation (Postgres SQLSTATE 23505), which the service catches and maps to apperrors.ErrConflict (409).

func (*Queries) JoinSection

func (q *Queries) JoinSection(ctx context.Context, arg JoinSectionParams) (CourseMember, error)

Adds the user to the section as a 'student'. ON CONFLICT DO NOTHING keeps duplicate joins atomic (no PK violation surfacing) and concurrency safe; the service layer treats an empty result as the 409 "Already a member of this section" case.

func (*Queries) LeaveSection

func (q *Queries) LeaveSection(ctx context.Context, arg LeaveSectionParams) (pgtype.UUID, error)

Hard-deletes the membership row. RETURNING lets the service detect a no-op delete (sql.ErrNoRows) and map it to the 404 "Not a member of this section" response.

func (*Queries) ListCourseSections

func (q *Queries) ListCourseSections(ctx context.Context, courseID pgtype.UUID) ([]ListCourseSectionsRow, error)

Returns sections with a live member_count via LEFT JOIN (so sections with zero members still appear). Ordered most-recent term first using start_date when present, falling back to section_code. NULLS LAST keeps sections without a known start_date at the bottom.

func (*Queries) ListCoursesCreatedAtAsc

func (q *Queries) ListCoursesCreatedAtAsc(ctx context.Context, arg ListCoursesCreatedAtAscParams) ([]ListCoursesCreatedAtAscRow, error)

func (*Queries) ListCoursesCreatedAtDesc

func (q *Queries) ListCoursesCreatedAtDesc(ctx context.Context, arg ListCoursesCreatedAtDescParams) ([]ListCoursesCreatedAtDescRow, error)

func (*Queries) ListCoursesDepartmentAsc

func (q *Queries) ListCoursesDepartmentAsc(ctx context.Context, arg ListCoursesDepartmentAscParams) ([]ListCoursesDepartmentAscRow, error)

All ListCourses* variants share the same WHERE template (school_id, department, q filters + sort-specific cursor) and only differ in ORDER BY direction and the cursor field shape. This mirrors the per-sort-variant pattern in files.sql -- sqlc cannot parameterize ORDER BY, so each direction gets its own named query.

The default sort (by department) uses a composite (department, number, id) cursor because (department) alone is not unique; the other sort fields use a simpler (field, id) cursor since the field is paired with the primary key as a tiebreaker.

func (*Queries) ListCoursesDepartmentDesc

func (q *Queries) ListCoursesDepartmentDesc(ctx context.Context, arg ListCoursesDepartmentDescParams) ([]ListCoursesDepartmentDescRow, error)

func (*Queries) ListCoursesNumberAsc

func (q *Queries) ListCoursesNumberAsc(ctx context.Context, arg ListCoursesNumberAscParams) ([]ListCoursesNumberAscRow, error)

func (*Queries) ListCoursesNumberDesc

func (q *Queries) ListCoursesNumberDesc(ctx context.Context, arg ListCoursesNumberDescParams) ([]ListCoursesNumberDescRow, error)

func (*Queries) ListCoursesTitleAsc

func (q *Queries) ListCoursesTitleAsc(ctx context.Context, arg ListCoursesTitleAscParams) ([]ListCoursesTitleAscRow, error)

func (*Queries) ListCoursesTitleDesc

func (q *Queries) ListCoursesTitleDesc(ctx context.Context, arg ListCoursesTitleDescParams) ([]ListCoursesTitleDescRow, error)

func (*Queries) ListGuideFiles

func (q *Queries) ListGuideFiles(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideFilesRow, error)

Attached files for the guide detail payload. Privacy floor: no user_id, no s3_key, no checksum. The file list shows only what a viewer needs to see: what's attached, what type, and how big.

Filters f.status = 'complete' so files mid-upload (pending) or failed don't surface in the guide detail -- a frontend that tried to download such a file would get a broken link. Only successfully uploaded files are visible to non-owners; the upload author's own file list (via the files endpoints) shows all statuses so they can retry or remove.

func (*Queries) ListGuideQuizzesWithQuestionCount

func (q *Queries) ListGuideQuizzesWithQuestionCount(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideQuizzesWithQuestionCountRow, error)

Non-deleted quizzes for the guide + question_count per quiz. The LEFT JOIN ensures quizzes with zero questions still appear with question_count = 0.

func (*Queries) ListGuideRecommenders

func (q *Queries) ListGuideRecommenders(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideRecommendersRow, error)

Recommenders list for the guide detail payload. Same privacy floor as CreatorSummary -- id + first_name + last_name only. Excludes recommenders whose user record is soft-deleted.

func (*Queries) ListGuideResources

func (q *Queries) ListGuideResources(ctx context.Context, studyGuideID pgtype.UUID) ([]ListGuideResourcesRow, error)

Attached resources for the guide detail payload. No creator info in the SELECT list -- the caller doesn't need to know who attached the resource.

func (*Queries) ListMyEnrollments

func (q *Queries) ListMyEnrollments(ctx context.Context, arg ListMyEnrollmentsParams) ([]ListMyEnrollmentsRow, error)

Returns every section a user is enrolled in with the compact course + school payload the dashboard renders. Optional term + role filters use sqlc.narg so the WHERE branch short-circuits when they're absent. Sort is fixed: most-recent term first (lexicographic "Spring 2026" > "Fall 2025" is acceptable for MVP per the spec), then department + number for stable in-term ordering.

func (*Queries) ListOwnedFilesCreatedAsc

func (q *Queries) ListOwnedFilesCreatedAsc(ctx context.Context, arg ListOwnedFilesCreatedAscParams) ([]ListOwnedFilesCreatedAscRow, error)

func (*Queries) ListOwnedFilesCreatedDesc

func (q *Queries) ListOwnedFilesCreatedDesc(ctx context.Context, arg ListOwnedFilesCreatedDescParams) ([]ListOwnedFilesCreatedDescRow, error)

func (*Queries) ListOwnedFilesMimeAsc

func (q *Queries) ListOwnedFilesMimeAsc(ctx context.Context, arg ListOwnedFilesMimeAscParams) ([]ListOwnedFilesMimeAscRow, error)

func (*Queries) ListOwnedFilesMimeDesc

func (q *Queries) ListOwnedFilesMimeDesc(ctx context.Context, arg ListOwnedFilesMimeDescParams) ([]ListOwnedFilesMimeDescRow, error)

func (*Queries) ListOwnedFilesNameAsc

func (q *Queries) ListOwnedFilesNameAsc(ctx context.Context, arg ListOwnedFilesNameAscParams) ([]ListOwnedFilesNameAscRow, error)

func (*Queries) ListOwnedFilesNameDesc

func (q *Queries) ListOwnedFilesNameDesc(ctx context.Context, arg ListOwnedFilesNameDescParams) ([]ListOwnedFilesNameDescRow, error)

func (*Queries) ListOwnedFilesSizeAsc

func (q *Queries) ListOwnedFilesSizeAsc(ctx context.Context, arg ListOwnedFilesSizeAscParams) ([]ListOwnedFilesSizeAscRow, error)

func (*Queries) ListOwnedFilesSizeDesc

func (q *Queries) ListOwnedFilesSizeDesc(ctx context.Context, arg ListOwnedFilesSizeDescParams) ([]ListOwnedFilesSizeDescRow, error)

func (*Queries) ListOwnedFilesStatusAsc

func (q *Queries) ListOwnedFilesStatusAsc(ctx context.Context, arg ListOwnedFilesStatusAscParams) ([]ListOwnedFilesStatusAscRow, error)

func (*Queries) ListOwnedFilesStatusDesc

func (q *Queries) ListOwnedFilesStatusDesc(ctx context.Context, arg ListOwnedFilesStatusDescParams) ([]ListOwnedFilesStatusDescRow, error)

func (*Queries) ListOwnedFilesUpdatedAsc

func (q *Queries) ListOwnedFilesUpdatedAsc(ctx context.Context, arg ListOwnedFilesUpdatedAscParams) ([]ListOwnedFilesUpdatedAscRow, error)

func (*Queries) ListOwnedFilesUpdatedDesc

func (q *Queries) ListOwnedFilesUpdatedDesc(ctx context.Context, arg ListOwnedFilesUpdatedDescParams) ([]ListOwnedFilesUpdatedDescRow, error)

func (*Queries) ListQuizAnswerOptionsByQuiz

func (q *Queries) ListQuizAnswerOptionsByQuiz(ctx context.Context, quizID pgtype.UUID) ([]QuizAnswerOption, error)

All answer options for every question in a quiz, ordered by question_id then sort_order then id. The mapper groups by question_id in Go to attach options to their parent question. The triple-key ordering keeps the option list deterministic.

func (*Queries) ListQuizQuestionsByQuiz

func (q *Queries) ListQuizQuestionsByQuiz(ctx context.Context, quizID pgtype.UUID) ([]ListQuizQuestionsByQuizRow, error)

All questions for a quiz, ordered by sort_order then id (the id tiebreaker keeps the response deterministic when two questions happen to share a sort_order -- the spec doesn't enforce uniqueness on sort_order). Returns reference_answer so the mapper can emit it as `correct_answer` on freeform questions.

func (*Queries) ListQuizzesByStudyGuide

func (q *Queries) ListQuizzesByStudyGuide(ctx context.Context, studyGuideID pgtype.UUID) ([]ListQuizzesByStudyGuideRow, error)

Lists every non-soft-deleted quiz attached to a study guide (ASK-136). Each row carries the privacy-floor creator payload (id + first_name + last_name only -- mirrors the studyguides surface) plus a server-computed question_count via LEFT JOIN + COUNT, so a quiz with zero questions still surfaces with a 0 count rather than being silently dropped by an INNER JOIN.

Soft-delete invariants:

  • q.deleted_at IS NULL -- excludes soft-deleted quizzes (AC3: studyguide with mixed live + deleted quizzes only returns the live ones)
  • u.deleted_at IS NULL -- excludes quizzes whose creator's user record was soft-deleted (matches the ASK-143 convention used by GetStudyGuideDetail)

The parent study guide's deleted_at is checked separately by the service via GuideExistsAndLiveForQuizzes BEFORE this query runs -- a missing or soft-deleted guide returns 404 even when this query would have returned an empty array. Keeps the 404 vs 200-empty distinction crisp.

Order: created_at DESC with id DESC as the deterministic tiebreaker (the spec calls for "newest first"). The id tiebreaker prevents a flaky test on a Postgres that happens to insert two rows in the same microsecond.

func (*Queries) ListSchools

func (q *Queries) ListSchools(ctx context.Context, arg ListSchoolsParams) ([]School, error)

func (*Queries) ListSectionMembers

func (q *Queries) ListSectionMembers(ctx context.Context, arg ListSectionMembersParams) ([]ListSectionMembersRow, error)

Returns the section roster joined against users for first/last name. Privacy floor: SELECT lists ONLY the five fields exposed in the SectionMemberResponse schema (user_id, first_name, last_name, role, joined_at). DO NOT add email, clerk_id, or any other user column to this list -- the endpoint is reachable by any authenticated user.

Soft-deleted users (users.deleted_at IS NOT NULL) are excluded -- the codebase's soft-delete convention is enforced by the partial indexes idx_users_deleted_at and idx_users_active_email. A user's soft-delete is the signal that they want to disappear from the product, so they must not surface in a public-by-design roster. The cursor still advances past them in the (joined_at, user_id) keyset, so removing them mid-iteration just shrinks the page rather than skipping live members.

Optional role filter via sqlc.narg short-circuits when absent. Keyset pagination on (joined_at, user_id) -- joined_at alone isn't unique (multiple users can join in the same second on a busy section), so user_id is the tiebreaker that keeps the keyset a strict total order.

func (*Queries) ListStudyGuidesNewestAsc

func (q *Queries) ListStudyGuidesNewestAsc(ctx context.Context, arg ListStudyGuidesNewestAscParams) ([]ListStudyGuidesNewestAscRow, error)

func (*Queries) ListStudyGuidesNewestDesc

func (q *Queries) ListStudyGuidesNewestDesc(ctx context.Context, arg ListStudyGuidesNewestDescParams) ([]ListStudyGuidesNewestDescRow, error)

func (*Queries) ListStudyGuidesScoreAsc

func (q *Queries) ListStudyGuidesScoreAsc(ctx context.Context, arg ListStudyGuidesScoreAscParams) ([]ListStudyGuidesScoreAscRow, error)

func (*Queries) ListStudyGuidesScoreDesc

func (q *Queries) ListStudyGuidesScoreDesc(ctx context.Context, arg ListStudyGuidesScoreDescParams) ([]ListStudyGuidesScoreDescRow, error)

Default sort. Multi-column keyset on (vote_score, view_count, updated_at, id) -- each column after vote_score is a tiebreaker for the previous one; id is the final strict-total-order tiebreaker.

func (*Queries) ListStudyGuidesUpdatedAsc

func (q *Queries) ListStudyGuidesUpdatedAsc(ctx context.Context, arg ListStudyGuidesUpdatedAscParams) ([]ListStudyGuidesUpdatedAscRow, error)

func (*Queries) ListStudyGuidesUpdatedDesc

func (q *Queries) ListStudyGuidesUpdatedDesc(ctx context.Context, arg ListStudyGuidesUpdatedDescParams) ([]ListStudyGuidesUpdatedDescRow, error)

func (*Queries) ListStudyGuidesViewsAsc

func (q *Queries) ListStudyGuidesViewsAsc(ctx context.Context, arg ListStudyGuidesViewsAscParams) ([]ListStudyGuidesViewsAscRow, error)

func (*Queries) ListStudyGuidesViewsDesc

func (q *Queries) ListStudyGuidesViewsDesc(ctx context.Context, arg ListStudyGuidesViewsDescParams) ([]ListStudyGuidesViewsDescRow, error)

func (*Queries) MarkFileDeleted

func (q *Queries) MarkFileDeleted(ctx context.Context, fileID pgtype.UUID) error

Called by the cleanup job handler once S3 deletion is confirmed.

func (*Queries) RevokeFileGrant

func (q *Queries) RevokeFileGrant(ctx context.Context, arg RevokeFileGrantParams) error

Deletes a file grant matching the exact composite key. No-op if the grant does not exist (idempotent).

func (*Queries) SectionInCourseExists

func (q *Queries) SectionInCourseExists(ctx context.Context, arg SectionInCourseExistsParams) (bool, error)

Verifies the section exists AND belongs to the supplied course. A section UUID that targets a different course is treated as not found to avoid leaking the existence of unrelated sections via the URL path.

func (*Queries) SetFileDeletionJobID

func (q *Queries) SetFileDeletionJobID(ctx context.Context, arg SetFileDeletionJobIDParams) error

Records the QStash message ID after publishing the async cleanup job.

func (*Queries) SoftDeleteFile

func (q *Queries) SoftDeleteFile(ctx context.Context, arg SoftDeleteFileParams) (int64, error)

Marks a file as pending deletion. Only applies if the file is owned by the caller and has not already entered a deletion state (idempotency-safe).

func (*Queries) SoftDeleteQuiz

func (q *Queries) SoftDeleteQuiz(ctx context.Context, id pgtype.UUID) error

Set deleted_at = now() on the quiz. The service has already verified the row exists, isn't already deleted, and the viewer is the creator -- so this is a blind UPDATE. No cascade: practice sessions, questions, and answer options are preserved per the ASK-102 spec ("preserve historical practice data; the quiz simply becomes invisible to list/detail endpoints").

func (*Queries) SoftDeleteQuizzesForGuide

func (q *Queries) SoftDeleteQuizzesForGuide(ctx context.Context, studyGuideID pgtype.UUID) error

Application-level cascade: soft-delete every non-deleted quiz on the guide. WHERE deleted_at IS NULL preserves the deleted_at timestamp on quizzes that were already soft-deleted before the guide was -- the spec explicitly requires that an already-deleted quiz's deleted_at is NOT updated by this cascade.

func (*Queries) SoftDeleteStudyGuide

func (q *Queries) SoftDeleteStudyGuide(ctx context.Context, id pgtype.UUID) error

Set deleted_at = now() on the guide. The service has already verified the row exists, isn't already deleted, and the viewer is the creator -- so this is a blind UPDATE. The DeleteStudyGuide transaction wraps this + SoftDeleteQuizzesForGuide.

func (*Queries) SoftDeleteUserByClerkID

func (q *Queries) SoftDeleteUserByClerkID(ctx context.Context, clerkID string) (int64, error)

func (*Queries) URLAlreadyAttachedToGuide

func (q *Queries) URLAlreadyAttachedToGuide(ctx context.Context, arg URLAlreadyAttachedToGuideParams) (bool, error)

Pre-flight conflict check for AttachResource (ASK-111). Returns TRUE when ANY resource with this URL is already attached to the given guide -- regardless of who created the resource row. Lets the service short-circuit to 409 BEFORE the resource upsert, so a duplicate attempt doesn't create or touch a resources row only to discard it on the join PK violation.

Why "regardless of creator": the join PK is (resource_id, study_guide_id), so two distinct resource rows (different creators but same URL) could both attach to the same guide without raising the join PK constraint. The spec treats that as a duplicate URL on the guide -- this query enforces the no-duplicate-URLs-per-guide contract at the application layer.

func (*Queries) UpdateFile

func (q *Queries) UpdateFile(ctx context.Context, arg UpdateFileParams) (UpdateFileRow, error)

Renames a file. Only applies if owned by the caller and not in a deletion state. Returns sql.ErrNoRows when file is not found, not owned, or in deletion.

func (*Queries) UpdateFileStatus

func (q *Queries) UpdateFileStatus(ctx context.Context, arg UpdateFileStatusParams) error

func (*Queries) UpdateQuiz

func (q *Queries) UpdateQuiz(ctx context.Context, arg UpdateQuizParams) error

Partial update for ASK-153. The title column uses the standard COALESCE(narg, current) pattern -- nil means "leave alone".

Description uses a CASE expression because COALESCE can't distinguish "field absent in the request" from "field explicitly null in the request" (both encode as SQL NULL once they reach the query layer). The handler resolves the tri-state, sets `clear_description` true on an explicit `description: null` body, and the service drives both args accordingly:

  • clear_description=false -> column unchanged
  • clear_description=true, description=NULL -> column cleared
  • clear_description=true, description="x" -> column set to "x"

The service is responsible for 404 / 403 gating before this query runs (via GetQuizForUpdateWithParentStatus). updated_at is bumped to now() on every successful call -- the at-least- one-field rule is enforced in Go so this query is never reached for an empty PATCH.

func (*Queries) UpdateStudyGuide

func (q *Queries) UpdateStudyGuide(ctx context.Context, arg UpdateStudyGuideParams) error

Partial update for ASK-129. Each updatable column uses COALESCE(narg, current) so a nil arg from Go means "leave this column alone" and a non-nil arg means "replace with the supplied value". The service is responsible for:

  • 404 / 403 gating (via GetStudyGuideByIDForUpdate before this).
  • Validating the at-least-one-field rule (an empty body is a 400 before this query runs).
  • Tag normalization (trim + lowercase + dedupe) -- the array written here is the final canonical form.

The service runs the locked SELECT + this UPDATE in a single transaction so a concurrent delete can't slip in between. updated_at is bumped to now() on every successful call (the UPDATE sees at least the updated_at change even when every other narg is NULL, which matches the spec's "updated_at reflects the latest" guarantee but also means a no-op PATCH still bumps updated_at -- the service's empty-body 400 check prevents that case from reaching SQL).

func (*Queries) UpsertClerkUser

func (q *Queries) UpsertClerkUser(ctx context.Context, arg UpsertClerkUserParams) (User, error)

func (*Queries) UpsertFileGrant

func (q *Queries) UpsertFileGrant(ctx context.Context, arg UpsertFileGrantParams) (FileGrant, error)

Inserts a new file grant, returning the row. If the grant already exists (same file_id, grantee_type, grantee_id, permission), updates granted_by and returns the row. Using DO UPDATE SET avoids a race window where concurrent inserts could cause both INSERT and fallback SELECT to miss.

func (*Queries) UpsertResource

func (q *Queries) UpsertResource(ctx context.Context, arg UpsertResourceParams) error

Inserts a new resources row for the (creator_id, url) pair. The ON CONFLICT DO NOTHING preserves the existing row's title / description / type when the viewer has used this URL before -- a silent overwrite would mutate state visible to the resource's other attachments (the same row may be attached to other guides + courses).

Paired with GetResourceByCreatorURL: the service runs both calls in sequence, then uses the SELECT'd row regardless of whether the INSERT actually wrote.

func (*Queries) UpsertStudyGuideVote

func (q *Queries) UpsertStudyGuideVote(ctx context.Context, arg UpsertStudyGuideVoteParams) error

Cast or change a vote (ASK-139). Inserts a new (user_id, study_guide_id, vote) row when the viewer has not voted, or updates the existing row's vote when the direction changes. Same- direction re-submits hit the WHERE clause on the DO UPDATE branch and become a true no-op (no row touched, no trigger fired, updated_at preserved). The (user_id, study_guide_id) PK from the schema is what makes ON CONFLICT resolve correctly.

func (*Queries) ViewerCanRecommendForGuide

func (q *Queries) ViewerCanRecommendForGuide(ctx context.Context, arg ViewerCanRecommendForGuideParams) (ViewerCanRecommendForGuideRow, error)

Combined live-presence + role-gate probe for the recommend endpoints (ASK-147 + ASK-101). Returns one row when the viewer holds instructor or ta role in AT LEAST ONE section of the guide's course AND the guide is live (not soft-deleted).

Returns three booleans so the service can distinguish 404 from 403 with a single round trip:

  • guide_exists -- guide row present AND deleted_at IS NULL
  • has_role -- viewer is instructor/ta in some section of the guide's course (ignored if guide doesn't exist)

Combining the two checks into a single query (rather than two sequential calls) keeps the recommend hot path at one DB round trip for the gate; the actual insert/delete is the second.

NULL-semantics note: when the guide doesn't exist, the inner `guide` CTE returns 0 rows, so `(SELECT course_id FROM guide)` is NULL, and `cs.course_id = NULL` is always FALSE (not NULL-equal). That makes `has_role` correctly false for missing guides without needing a separate WHERE EXISTS guard. The service short-circuits on !guide_exists before inspecting has_role, so the two booleans are independent by contract even though they correlate in this edge case.

func (*Queries) WithTx

func (q *Queries) WithTx(tx pgx.Tx) *Queries

type QuestionType

type QuestionType string
const (
QuestionTypeMultipleChoice QuestionType = "multiple_choice"
QuestionTypeTrueFalse QuestionType = "true_false"
QuestionTypeFreeform QuestionType = "freeform"
)

func (*QuestionType) Scan

func (e *QuestionType) Scan(src interface{}) error

type Quiz

type Quiz struct {
ID pgtype.UUID `json:"id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
}

type QuizAnswerOption

type QuizAnswerOption struct {
ID pgtype.UUID `json:"id"`
QuestionID pgtype.UUID `json:"question_id"`
Text string `json:"text"`
IsCorrect bool `json:"is_correct"`
SortOrder int32 `json:"sort_order"`
}

type QuizQuestion

type QuizQuestion struct {
ID pgtype.UUID `json:"id"`
QuizID pgtype.UUID `json:"quiz_id"`
Type QuestionType `json:"type"`
QuestionText string `json:"question_text"`
Hint pgtype.Text `json:"hint"`
FeedbackCorrect pgtype.Text `json:"feedback_correct"`
FeedbackIncorrect pgtype.Text `json:"feedback_incorrect"`
ReferenceAnswer pgtype.Text `json:"reference_answer"`
IsProtected bool `json:"is_protected"`
SortOrder int32 `json:"sort_order"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type Resource

type Resource struct {
ID pgtype.UUID `json:"id"`
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Url string `json:"url"`
Description pgtype.Text `json:"description"`
Type ResourceType `json:"type"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type ResourceType

type ResourceType string
const (
ResourceTypeLink ResourceType = "link"
ResourceTypeVideo ResourceType = "video"
ResourceTypeArticle ResourceType = "article"
ResourceTypePdf ResourceType = "pdf"
)

func (*ResourceType) Scan

func (e *ResourceType) Scan(src interface{}) error

type RevokeFileGrantParams

type RevokeFileGrantParams struct {
FileID pgtype.UUID `json:"file_id"`
GranteeType GranteeType `json:"grantee_type"`
GranteeID pgtype.UUID `json:"grantee_id"`
Permission Permission `json:"permission"`
}

type School

type School struct {
ID pgtype.UUID `json:"id"`
Name string `json:"name"`
Acronym string `json:"acronym"`
Domain pgtype.Text `json:"domain"`
Url pgtype.Text `json:"url"`
City pgtype.Text `json:"city"`
State pgtype.Text `json:"state"`
Country pgtype.Text `json:"country"`
IpedsID pgtype.Text `json:"ipeds_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type SectionInCourseExistsParams

type SectionInCourseExistsParams struct {
SectionID pgtype.UUID `json:"section_id"`
CourseID pgtype.UUID `json:"course_id"`
}

type SetFileDeletionJobIDParams

type SetFileDeletionJobIDParams struct {
JobID pgtype.Text `json:"job_id"`
FileID pgtype.UUID `json:"file_id"`
}

type SoftDeleteFileParams

type SoftDeleteFileParams struct {
FileID pgtype.UUID `json:"file_id"`
OwnerID pgtype.UUID `json:"owner_id"`
}

type StudyGuide

type StudyGuide struct {
ID pgtype.UUID `json:"id"`
CourseID pgtype.UUID `json:"course_id"`
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Description pgtype.Text `json:"description"`
Content pgtype.Text `json:"content"`
Tags []string `json:"tags"`
ViewCount int32 `json:"view_count"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
}

type StudyGuideFavorite

type StudyGuideFavorite struct {
UserID pgtype.UUID `json:"user_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type StudyGuideFile

type StudyGuideFile struct {
FileID pgtype.UUID `json:"file_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type StudyGuideLastViewed

type StudyGuideLastViewed struct {
UserID pgtype.UUID `json:"user_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
ViewedAt pgtype.Timestamptz `json:"viewed_at"`
}

type StudyGuideRecommendation

type StudyGuideRecommendation struct {
StudyGuideID pgtype.UUID `json:"study_guide_id"`
RecommendedBy pgtype.UUID `json:"recommended_by"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type StudyGuideResource

type StudyGuideResource struct {
ResourceID pgtype.UUID `json:"resource_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
AttachedBy pgtype.UUID `json:"attached_by"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
}

type StudyGuideVote

type StudyGuideVote struct {
UserID pgtype.UUID `json:"user_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
Vote VoteDirection `json:"vote"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type URLAlreadyAttachedToGuideParams

type URLAlreadyAttachedToGuideParams struct {
StudyGuideID pgtype.UUID `json:"study_guide_id"`
Url string `json:"url"`
}

type UpdateFileParams

type UpdateFileParams struct {
Name string `json:"name"`
FileID pgtype.UUID `json:"file_id"`
OwnerID pgtype.UUID `json:"owner_id"`
}

type UpdateFileRow

type UpdateFileRow struct {
ID pgtype.UUID `json:"id"`
UserID pgtype.UUID `json:"user_id"`
Name string `json:"name"`
Size int64 `json:"size"`
MimeType string `json:"mime_type"`
Status UploadStatus `json:"status"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
}

type UpdateFileStatusParams

type UpdateFileStatusParams struct {
Status UploadStatus `json:"status"`
FileID pgtype.UUID `json:"file_id"`
OwnerID pgtype.UUID `json:"owner_id"`
}

type UpdateQuizParams

type UpdateQuizParams struct {
Title pgtype.Text `json:"title"`
ClearDescription bool `json:"clear_description"`
Description pgtype.Text `json:"description"`
ID pgtype.UUID `json:"id"`
}

type UpdateStudyGuideParams

type UpdateStudyGuideParams struct {
Title pgtype.Text `json:"title"`
Description pgtype.Text `json:"description"`
Content pgtype.Text `json:"content"`
Tags []string `json:"tags"`
ID pgtype.UUID `json:"id"`
}

type UploadStatus

type UploadStatus string
const (
UploadStatusPending UploadStatus = "pending"
UploadStatusComplete UploadStatus = "complete"
UploadStatusFailed UploadStatus = "failed"
)

func (*UploadStatus) Scan

func (e *UploadStatus) Scan(src interface{}) error

type UpsertClerkUserParams

type UpsertClerkUserParams struct {
ClerkID string `json:"clerk_id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
MiddleName pgtype.Text `json:"middle_name"`
Metadata interface{} `json:"metadata"`
}

type UpsertFileGrantParams

type UpsertFileGrantParams struct {
FileID pgtype.UUID `json:"file_id"`
GranteeType GranteeType `json:"grantee_type"`
GranteeID pgtype.UUID `json:"grantee_id"`
Permission Permission `json:"permission"`
GrantedBy pgtype.UUID `json:"granted_by"`
}

type UpsertResourceParams

type UpsertResourceParams struct {
CreatorID pgtype.UUID `json:"creator_id"`
Title string `json:"title"`
Url string `json:"url"`
Description pgtype.Text `json:"description"`
Type ResourceType `json:"type"`
}

type UpsertStudyGuideVoteParams

type UpsertStudyGuideVoteParams struct {
UserID pgtype.UUID `json:"user_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
Vote VoteDirection `json:"vote"`
}

type User

type User struct {
ID pgtype.UUID `json:"id"`
ClerkID string `json:"clerk_id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
MiddleName pgtype.Text `json:"middle_name"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
Metadata []byte `json:"metadata"`
}

type ViewerCanRecommendForGuideParams

type ViewerCanRecommendForGuideParams struct {
ViewerID pgtype.UUID `json:"viewer_id"`
StudyGuideID pgtype.UUID `json:"study_guide_id"`
}

type ViewerCanRecommendForGuideRow

type ViewerCanRecommendForGuideRow struct {
GuideExists bool `json:"guide_exists"`
HasRole bool `json:"has_role"`
}

type VoteDirection

type VoteDirection string
const (
VoteDirectionUp VoteDirection = "up"
VoteDirectionDown VoteDirection = "down"
)

func (*VoteDirection) Scan

func (e *VoteDirection) Scan(src interface{}) error

Generated by gomarkdoc