package repository import ( "context" "errors" "git.sch9.ru/new_gate/ms-tester/internal/models" "git.sch9.ru/new_gate/ms-tester/pkg/utils" "github.com/jackc/pgerrcode" "github.com/jackc/pgx/v5/pgconn" "github.com/jmoiron/sqlx" "go.uber.org/zap" ) type ContestRepository struct { db *sqlx.DB logger *zap.Logger } func NewContestRepository(db *sqlx.DB, logger *zap.Logger) *ContestRepository { return &ContestRepository{ db: db, logger: logger, } } const createContestQuery = "INSERT INTO contest (title) VALUES (?) RETURNING id" func (r *ContestRepository) CreateContest(ctx context.Context, title string) (int32, error) { query := r.db.Rebind(createContestQuery) rows, err := r.db.QueryxContext(ctx, query, title) if err != nil { return 0, handlePgErr(err) } defer rows.Close() var id int32 rows.Next() err = rows.Scan(&id) if err != nil { return 0, handlePgErr(err) } return id, nil } const readContestByIdQuery = "SELECT * from contest WHERE id=? LIMIT 1" func (r *ContestRepository) ReadContestById(ctx context.Context, id int32) (*models.Contest, error) { var contest models.Contest query := r.db.Rebind(readContestByIdQuery) err := r.db.GetContext(ctx, &contest, query, id) if err != nil { return nil, handlePgErr(err) } return &contest, nil } const deleteContestQuery = "DELETE FROM contest WHERE id=?" func (r *ContestRepository) DeleteContest(ctx context.Context, id int32) error { query := r.db.Rebind(deleteContestQuery) _, err := r.db.ExecContext(ctx, query, id) if err != nil { return handlePgErr(err) } return nil } const addTaskQuery = "INSERT INTO task (problem_id, contest_id, position) VALUES (?, ?,COALESCE(SELECT MAX(position) FROM task WHERE contest_id = ? ,0) + 1) RETURNING id" func (r *ContestRepository) AddTask(ctx context.Context, contestId int32, problem_id int32) (int32, error) { query := r.db.Rebind(addTaskQuery) rows, err := r.db.QueryxContext(ctx, query, problem_id, contestId, contestId) if err != nil { return 0, handlePgErr(err) } defer rows.Close() var id int32 rows.Next() err = rows.Scan(&id) if err != nil { return 0, handlePgErr(err) } return id, nil } const deleteTaskQuery = "DELETE FROM task WHERE id=?" func (r *ContestRepository) DeleteTask(ctx context.Context, taskId int32) error { query := r.db.Rebind(deleteTaskQuery) _, err := r.db.ExecContext(ctx, query, taskId) if err != nil { return handlePgErr(err) } return nil } const addParticipantQuery = "INSERT INTO participant (user_id ,contest_id, name) VALUES (?, ?, ?) RETURNING id" func (r *ContestRepository) AddParticipant(ctx context.Context, contestId int32, userId int32) (int32, error) { query := r.db.Rebind(addParticipantQuery) name := "" rows, err := r.db.QueryxContext(ctx, query, contestId, userId, name) if err != nil { return 0, handlePgErr(err) } defer rows.Close() var id int32 rows.Next() err = rows.Scan(&id) if err != nil { return 0, err } return id, nil } const deleteParticipantQuery = "DELETE FROM participant WHERE id=?" func (r *ContestRepository) DeleteParticipant(ctx context.Context, participantId int32) error { query := r.db.Rebind(deleteParticipantQuery) _, err := r.db.ExecContext(ctx, query, participantId) if err != nil { return handlePgErr(err) } return nil } func handlePgErr(err error) error { var pgErr *pgconn.PgError if !errors.As(err, &pgErr) { return utils.StorageError(err, utils.ErrUnknown, "unexpected error from postgres") } if pgerrcode.IsIntegrityConstraintViolation(pgErr.Code) { // TODO: probably should specify which constraint return utils.StorageError(err, utils.ErrConflict, pgErr.Message) } if pgerrcode.IsNoData(pgErr.Code) { return utils.StorageError(err, utils.ErrNotFound, pgErr.Message) } return utils.StorageError(err, utils.ErrUnimplemented, "unimplemented error") }