Compare commits

...

11 commits

18 changed files with 6994 additions and 25 deletions

1958
api/gen.go Normal file

File diff suppressed because it is too large Load diff

6
api/oapi-codegen.yaml Normal file
View file

@ -0,0 +1,6 @@
package: api
generate:
strict-server: true
gin-server: true
models: true
output: gen.go

1958
modules/backend/api/gen.go Normal file

File diff suppressed because it is too large Load diff

718
modules/backend/api/impl.go Normal file
View file

@ -0,0 +1,718 @@
package api
import (
"context"
"encoding/json"
"nyanimedb-server/db"
"strconv"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"golang.org/x/crypto/bcrypt"
)
type Server struct {
db *db.Queries
}
func NewServer(db *db.Queries) Server {
return Server{db: db}
}
// —————————————————————————————————————————————
// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
// —————————————————————————————————————————————
func parseInt32(s string) (int32, error) {
i, err := strconv.ParseInt(s, 10, 32)
return int32(i), err
}
func ptr[T any](v T) *T { return &v }
func pgInt4ToPtr(v pgtype.Int4) *int32 {
if v.Valid {
return &v.Int32
}
return nil
}
func pgTextToPtr(v pgtype.Text) *string {
if v.Valid {
return &v.String
}
return nil
}
func pgFloat8ToPtr(v pgtype.Float8) *float64 {
if v.Valid {
return &v.Float64
}
return nil
}
func jsonbToInterface(data []byte) interface{} {
if data == nil {
return nil
}
var out interface{}
if err := json.Unmarshal(data, &out); err != nil {
return string(data) // fallback
}
return out
}
// —————————————————————————————————————————————
// ХЕНДЛЕРЫ
// —————————————————————————————————————————————
func (s Server) GetMedia(ctx context.Context, req GetMediaRequestObject) (GetMediaResponseObject, error) {
id, err := parseInt32(req.Params.ImageId)
if err != nil {
return GetMedia200JSONResponse{Success: ptr(false), Error: ptr("invalid image_id")}, nil
}
img, err := s.db.GetImageByID(ctx, id)
if err != nil {
if err == pgx.ErrNoRows {
return GetMedia200JSONResponse{Success: ptr(false), Error: ptr("image not found")}, nil
}
return nil, err
}
return GetMedia200JSONResponse{
Success: ptr(true),
ImagePath: ptr(img.ImagePath),
}, nil
}
func (s Server) PostMedia(ctx context.Context, req PostMediaRequestObject) (PostMediaResponseObject, error) {
// ❗ Не реализовано: OpenAPI не определяет тело запроса для загрузки
return PostMedia200JSONResponse{
Success: ptr(false),
Error: ptr("upload not implemented: request body not defined in spec"),
}, nil
}
func (s Server) GetUsers(ctx context.Context, req GetUsersRequestObject) (GetUsersResponseObject, error) {
users, err := s.db.ListUsers(ctx, db.ListUsersParams{})
if err != nil {
return nil, err
}
var resp []User
for _, u := range users {
resp = append(resp, mapUser(u))
}
return GetUsers200JSONResponse(resp), nil
}
func (s Server) PostUsers(ctx context.Context, req PostUsersRequestObject) (PostUsersResponseObject, error) {
if req.Body == nil {
return PostUsers200JSONResponse{
Success: ptr(false),
Error: ptr("request body is required"),
}, nil
}
body := *req.Body
// Обязательные поля
nickname, ok := body["nickname"].(string)
if !ok || nickname == "" {
return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("nickname is required")}, nil
}
mail, ok := body["mail"].(string)
if !ok || mail == "" {
return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("mail is required")}, nil
}
password, ok := body["password"].(string)
if !ok || password == "" {
return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("password is required")}, nil
}
// Опциональные поля
var avatarID *int32
if v, ok := body["avatar_id"].(float64); ok {
id := int32(v)
avatarID = &id
}
dispName, _ := body["disp_name"].(string)
userDesc, _ := body["user_desc"].(string)
// 🔐 Хешируем пароль
passhashBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("failed to hash password")}, nil
}
passhash := string(passhashBytes)
// Сохраняем в БД
_, err = s.db.CreateUser(ctx, db.CreateUserParams{
AvatarID: pgtype.Int4{
Int32: 0,
Valid: avatarID != nil,
},
Passhash: passhash,
Mail: pgtype.Text{
String: mail,
Valid: true,
},
Nickname: nickname,
DispName: pgtype.Text{
String: dispName,
Valid: dispName != "",
},
UserDesc: pgtype.Text{
String: userDesc,
Valid: userDesc != "",
},
CreationDate: pgtype.Timestamp{
Time: time.Now(),
Valid: true,
},
})
if err != nil {
// Проверяем нарушение уникальности (например, дубль mail или nickname)
if err.Error() == "ERROR: duplicate key value violates unique constraint \"users_mail_key\"" ||
err.Error() == "ERROR: duplicate key value violates unique constraint \"users_nickname_key\"" {
return PostUsers200JSONResponse{
Success: ptr(false),
Error: ptr("user with this email or nickname already exists"),
}, nil
}
return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("database error")}, nil
}
// Получаем созданного пользователя (без passhash и mail!)
// Предположим, что у вас есть запрос GetUserByNickname или аналогичный
// Но проще — вернуть только ID и nickname
// ⚠️ Поскольку мы не знаем user_id, можно:
// а) добавить RETURNING в CreateUser (рекомендуется),
// б) сделать отдельный SELECT.
// Пока вернём минимальный ответ
userResp := User{
"nickname": nickname,
// "user_id" можно добавить, если обновите query.sql
}
return PostUsers200JSONResponse{
Success: ptr(true),
UserJson: &userResp,
}, nil
}
func (s Server) DeleteUsersUserId(ctx context.Context, req DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return DeleteUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil
}
err = s.db.DeleteUser(ctx, userID)
if err != nil {
if err == pgx.ErrNoRows {
return DeleteUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("user not found")}, nil
}
return nil, err
}
return DeleteUsersUserId200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) GetUsersUserId(ctx context.Context, req GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return GetUsersUserId404Response{}, nil
}
user, err := s.db.GetUserByID(ctx, userID)
if err != nil {
if err == pgx.ErrNoRows {
return GetUsersUserId404Response{}, nil
}
return nil, err
}
return GetUsersUserId200JSONResponse(mapUser(user)), nil
}
func (s Server) PatchUsersUserId(ctx context.Context, req PatchUsersUserIdRequestObject) (PatchUsersUserIdResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil
}
if req.Body == nil {
return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
}
body := *req.Body
args := db.UpdateUserParams{
UserID: userID,
}
if v, ok := body["avatar_id"].(float64); ok {
args.AvatarID = pgtype.Int4{Int32: int32(v), Valid: true}
// args.AvatarIDValid = true
}
if v, ok := body["disp_name"].(string); ok {
args.DispName = pgtype.Text{String: v, Valid: true}
// args.DispNameValid = true
}
if v, ok := body["user_desc"].(string); ok {
args.UserDesc = pgtype.Text{String: v, Valid: true}
// args.UserDescValid = true
}
_, err = s.db.UpdateUser(ctx, args)
if err != nil {
return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
}
return PatchUsersUserId200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) GetUsersUserIdReviews(ctx context.Context, req GetUsersUserIdReviewsRequestObject) (GetUsersUserIdReviewsResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return GetUsersUserIdReviews200JSONResponse{}, nil
}
limit := int32(20)
offset := int32(0)
// if req.Params.Limit != nil {
// limit = int32(*req.Params.Limit)
// }
// if req.Params.Offset != nil {
// offset = int32(*req.Params.Offset)
// }
reviews, err := s.db.ListReviewsByUser(ctx, db.ListReviewsByUserParams{
UserID: userID,
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var resp []Review
for _, r := range reviews {
resp = append(resp, mapReview(r))
}
return GetUsersUserIdReviews200JSONResponse(resp), nil
}
func (s Server) DeleteUsersUserIdTitles(ctx context.Context, req DeleteUsersUserIdTitlesRequestObject) (DeleteUsersUserIdTitlesResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil
}
if req.Params.TitleId != nil {
titleID, err := parseInt32(*req.Params.TitleId)
if err != nil {
return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil
}
err = s.db.DeleteUserTitle(ctx, db.DeleteUserTitleParams{
UserID: userID,
Column2: titleID,
})
if err != nil && err != pgx.ErrNoRows {
return nil, err
}
}
// else {
// err = s.db.DeleteAllUserTitles(ctx, userID)
// if err != nil {
// return nil, err
// }
// }
return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) GetUsersUserIdTitles(ctx context.Context, req GetUsersUserIdTitlesRequestObject) (GetUsersUserIdTitlesResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return GetUsersUserIdTitles200JSONResponse{}, nil
}
limit := int32(100)
offset := int32(0)
if req.Params.Limit != nil {
limit = int32(*req.Params.Limit)
}
if req.Params.Offset != nil {
offset = int32(*req.Params.Offset)
}
titles, err := s.db.ListUserTitles(ctx, db.ListUserTitlesParams{
UserID: userID,
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var resp []UserTitle
for _, t := range titles {
resp = append(resp, mapUserTitle(t))
}
return GetUsersUserIdTitles200JSONResponse(resp), nil
}
func (s Server) PatchUsersUserIdTitles(ctx context.Context, req PatchUsersUserIdTitlesRequestObject) (PatchUsersUserIdTitlesResponseObject, error) {
// userID, err := parseInt32(req.UserId)
// if err != nil {
// return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil
// }
// if req.Body == nil {
// return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
// }
// body := *req.Body
// titleID, ok := body["title_id"].(float64)
// if !ok {
// return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("title_id required")}, nil
// }
// args := db.UpdateUserTitleParams{
// UserID: userID,
// TitleID: int32(titleID),
// }
// if v, ok := body["status"].(string); ok {
// args.Status = db.UsertitleStatusT(v)
// // args.StatusValid = true
// }
// if v, ok := body["rate"].(float64); ok {
// args.Rate = pgtype.Int4{Int32: int32(v), Valid: true}
// // args.RateValid = true
// }
// if v, ok := body["review_id"].(float64); ok {
// args.ReviewID = pgtype.Int4{Int32: int32(v), Valid: true}
// // args.ReviewIDValid = true
// }
// _, err = s.db.UpdateUserTitle(ctx, args)
// if err != nil {
// return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
// }
return PatchUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) PostUsersUserIdTitles(ctx context.Context, req PostUsersUserIdTitlesRequestObject) (PostUsersUserIdTitlesResponseObject, error) {
userID, err := parseInt32(req.UserId)
if err != nil {
return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil
}
if req.Body == nil {
return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
}
body := req.Body
titleID, err := parseInt32(*body.TitleId)
if err != nil {
return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil
}
status := db.UsertitleStatusT("planned")
if body.Status != nil {
status = db.UsertitleStatusT(*body.Status)
}
_, err = s.db.CreateUserTitle(ctx, db.CreateUserTitleParams{
UserID: userID,
TitleID: titleID,
Status: status,
Rate: pgtype.Int4{Valid: false},
ReviewID: pgtype.Int4{Valid: false},
})
if err != nil {
return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
}
return PostUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) GetTags(ctx context.Context, req GetTagsRequestObject) (GetTagsResponseObject, error) {
limit := int32(100)
offset := int32(0)
if req.Params.Limit != nil {
limit = int32(*req.Params.Limit)
}
if req.Params.Offset != nil {
offset = int32(*req.Params.Offset)
}
tags, err := s.db.ListTags(ctx, db.ListTagsParams{Limit: limit, Offset: offset})
if err != nil {
return nil, err
}
var resp []Tag
for _, t := range tags {
resp = append(resp, Tag{
"tag_id": t.TagID,
"tag_names": jsonbToInterface(t.TagNames),
})
}
return GetTags200JSONResponse(resp), nil
}
func (s Server) GetTitle(ctx context.Context, req GetTitleRequestObject) (GetTitleResponseObject, error) {
limit := int32(50)
offset := int32(0)
if req.Params.Limit != nil {
limit = int32(*req.Params.Limit)
}
if req.Params.Offset != nil {
offset = int32(*req.Params.Offset)
}
titles, err := s.db.ListTitles(ctx, db.ListTitlesParams{Limit: limit, Offset: offset})
if err != nil {
return nil, err
}
var resp []Title
for _, t := range titles {
resp = append(resp, mapTitle(t))
}
return GetTitle200JSONResponse(resp), nil
}
func (s Server) GetTitleTitleId(ctx context.Context, req GetTitleTitleIdRequestObject) (GetTitleTitleIdResponseObject, error) {
titleID, err := parseInt32(req.TitleId)
if err != nil {
return GetTitleTitleId404Response{}, nil
}
title, err := s.db.GetTitleByID(ctx, titleID)
if err != nil {
if err == pgx.ErrNoRows {
return GetTitleTitleId404Response{}, nil
}
return nil, err
}
return GetTitleTitleId200JSONResponse(mapTitle(title)), nil
}
func (s Server) PatchTitleTitleId(ctx context.Context, req PatchTitleTitleIdRequestObject) (PatchTitleTitleIdResponseObject, error) {
titleID, err := parseInt32(req.TitleId)
if err != nil {
return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil
}
if req.Body == nil {
return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
}
body := *req.Body
args := db.UpdateTitleParams{
TitleID: titleID,
}
if v, ok := body["title_names"].(map[string]interface{}); ok {
data, _ := json.Marshal(v)
args.TitleNames = data
// args.TitleNamesValid = true
}
if v, ok := body["studio_id"].(float64); ok {
args.StudioID = pgtype.Int4{Int32: int32(v), Valid: true}
// args.StudioIDValid = true
}
if v, ok := body["poster_id"].(float64); ok {
args.PosterID = pgtype.Int4{Int32: int32(v), Valid: true}
// args.PosterIDValid = true
}
// if v, ok := body["title_status"].(string); ok {
// args.TitleStatus = db.NullTitleStatusT(v)
// // args.TitleStatusValid = true
// }
if v, ok := body["release_year"].(float64); ok {
args.ReleaseYear = pgtype.Int4{Int32: int32(v), Valid: true}
// args.ReleaseYearValid = true
}
if v, ok := body["episodes_aired"].(float64); ok {
args.EpisodesAired = pgtype.Int4{Int32: int32(v), Valid: true}
// args.EpisodesAiredValid = true
}
if v, ok := body["episodes_all"].(float64); ok {
args.EpisodesAll = pgtype.Int4{Int32: int32(v), Valid: true}
// args.EpisodesAllValid = true
}
_, err = s.db.UpdateTitle(ctx, args)
if err != nil {
return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
}
return PatchTitleTitleId200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) GetTitleTitleIdReviews(ctx context.Context, req GetTitleTitleIdReviewsRequestObject) (GetTitleTitleIdReviewsResponseObject, error) {
titleID, err := parseInt32(req.TitleId)
if err != nil {
return GetTitleTitleIdReviews200JSONResponse{}, nil
}
limit := int32(20)
offset := int32(0)
if req.Params.Limit != nil {
limit = int32(*req.Params.Limit)
}
if req.Params.Offset != nil {
offset = int32(*req.Params.Offset)
}
reviews, err := s.db.ListReviewsByTitle(ctx, db.ListReviewsByTitleParams{
TitleID: titleID,
Limit: limit,
Offset: offset,
})
if err != nil {
return nil, err
}
var resp []Review
for _, r := range reviews {
resp = append(resp, mapReview(r))
}
return GetTitleTitleIdReviews200JSONResponse(resp), nil
}
func (s Server) PostReviews(ctx context.Context, req PostReviewsRequestObject) (PostReviewsResponseObject, error) {
if req.Body == nil {
return PostReviews200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
}
body := *req.Body
userID, ok1 := body["user_id"].(float64)
titleID, ok2 := body["title_id"].(float64)
reviewText, ok3 := body["review_text"].(string)
if !ok1 || !ok2 || !ok3 {
return PostReviews200JSONResponse{Success: ptr(false), Error: ptr("user_id, title_id, review_text required")}, nil
}
var imageIDs []int32
if ids, ok := body["image_ids"].([]interface{}); ok {
for _, id := range ids {
if f, ok := id.(float64); ok {
imageIDs = append(imageIDs, int32(f))
}
}
}
_, err := s.db.CreateReview(ctx, db.CreateReviewParams{
UserID: int32(userID),
TitleID: int32(titleID),
ImageIds: imageIDs,
ReviewText: reviewText,
CreationDate: pgtype.Timestamp{Time: time.Now(), Valid: true},
})
if err != nil {
return PostReviews200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
}
return PostReviews200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) DeleteReviewsReviewId(ctx context.Context, req DeleteReviewsReviewIdRequestObject) (DeleteReviewsReviewIdResponseObject, error) {
reviewID, err := parseInt32(req.ReviewId)
if err != nil {
return DeleteReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("invalid review_id")}, nil
}
err = s.db.DeleteReview(ctx, reviewID)
if err != nil {
if err == pgx.ErrNoRows {
return DeleteReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("review not found")}, nil
}
return nil, err
}
return DeleteReviewsReviewId200JSONResponse{Success: ptr(true)}, nil
}
func (s Server) PatchReviewsReviewId(ctx context.Context, req PatchReviewsReviewIdRequestObject) (PatchReviewsReviewIdResponseObject, error) {
reviewID, err := parseInt32(req.ReviewId)
if err != nil {
return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("invalid review_id")}, nil
}
if req.Body == nil {
return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil
}
body := *req.Body
args := db.UpdateReviewParams{
ReviewID: reviewID,
}
if v, ok := body["review_text"].(string); ok {
args.ReviewText = pgtype.Text{String: v, Valid: true}
// args.ReviewTextValid = true
}
if ids, ok := body["image_ids"].([]interface{}); ok {
var imageIDs []int32
for _, id := range ids {
if f, ok := id.(float64); ok {
imageIDs = append(imageIDs, int32(f))
}
}
args.ImageIds = imageIDs
// args.ImageIdsValid = true
}
_, err = s.db.UpdateReview(ctx, args)
if err != nil {
return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil
}
return PatchReviewsReviewId200JSONResponse{Success: ptr(true)}, nil
}
// —————————————————————————————————————————————
// МАППИНГИ
// —————————————————————————————————————————————
func mapUser(u db.Users) User {
return User{
"user_id": u.UserID,
"avatar_id": pgInt4ToPtr(u.AvatarID),
"nickname": u.Nickname,
"disp_name": pgTextToPtr(u.DispName),
"user_desc": pgTextToPtr(u.UserDesc),
"creation_date": u.CreationDate.Time,
// mail и passhash НЕ возвращаем!
}
}
func mapTitle(t db.Titles) Title {
var releaseSeason interface{}
if t.ReleaseSeason.Valid {
releaseSeason = string(t.ReleaseSeason.ReleaseSeasonT)
}
return Title{
"title_id": t.TitleID,
"title_names": jsonbToInterface(t.TitleNames),
"studio_id": t.StudioID,
"poster_id": pgInt4ToPtr(t.PosterID),
"signal_ids": t.SignalIds,
"title_status": string(t.TitleStatus),
"rating": pgFloat8ToPtr(t.Rating),
"rating_count": pgInt4ToPtr(t.RatingCount),
"release_year": pgInt4ToPtr(t.ReleaseYear),
"release_season": releaseSeason,
"season": pgInt4ToPtr(t.Season),
"episodes_aired": pgInt4ToPtr(t.EpisodesAired),
"episodes_all": pgInt4ToPtr(t.EpisodesAll),
"episodes_len": jsonbToInterface(t.EpisodesLen),
}
}
func mapReview(r db.Reviews) Review {
return Review{
"review_id": r.ReviewID,
"user_id": r.UserID,
"title_id": r.TitleID,
"image_ids": r.ImageIds,
"review_text": r.ReviewText,
"creation_date": r.CreationDate.Time,
}
}
func mapUserTitle(ut db.Usertitles) UserTitle {
return UserTitle{
"usertitle_id": ut.UsertitleID,
"user_id": ut.UserID,
"title_id": ut.TitleID,
"status": string(ut.Status),
"rate": pgInt4ToPtr(ut.Rate),
"review_id": pgInt4ToPtr(ut.ReviewID),
}
}

32
modules/backend/db/db.go Normal file
View file

@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package db
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
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
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

View file

@ -0,0 +1,266 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package db
import (
"database/sql/driver"
"fmt"
"github.com/jackc/pgx/v5/pgtype"
)
type ReleaseSeasonT string
const (
ReleaseSeasonTWinter ReleaseSeasonT = "winter"
ReleaseSeasonTSpring ReleaseSeasonT = "spring"
ReleaseSeasonTSummer ReleaseSeasonT = "summer"
ReleaseSeasonTFall ReleaseSeasonT = "fall"
)
func (e *ReleaseSeasonT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = ReleaseSeasonT(s)
case string:
*e = ReleaseSeasonT(s)
default:
return fmt.Errorf("unsupported scan type for ReleaseSeasonT: %T", src)
}
return nil
}
type NullReleaseSeasonT struct {
ReleaseSeasonT ReleaseSeasonT `json:"release_season_t"`
Valid bool `json:"valid"` // Valid is true if ReleaseSeasonT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullReleaseSeasonT) Scan(value interface{}) error {
if value == nil {
ns.ReleaseSeasonT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.ReleaseSeasonT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullReleaseSeasonT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.ReleaseSeasonT), nil
}
type StorageTypeT string
const (
StorageTypeTLocal StorageTypeT = "local"
StorageTypeTS3 StorageTypeT = "s3"
)
func (e *StorageTypeT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = StorageTypeT(s)
case string:
*e = StorageTypeT(s)
default:
return fmt.Errorf("unsupported scan type for StorageTypeT: %T", src)
}
return nil
}
type NullStorageTypeT struct {
StorageTypeT StorageTypeT `json:"storage_type_t"`
Valid bool `json:"valid"` // Valid is true if StorageTypeT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullStorageTypeT) Scan(value interface{}) error {
if value == nil {
ns.StorageTypeT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.StorageTypeT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullStorageTypeT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.StorageTypeT), nil
}
type TitleStatusT string
const (
TitleStatusTFinished TitleStatusT = "finished"
TitleStatusTOngoing TitleStatusT = "ongoing"
TitleStatusTPlanned TitleStatusT = "planned"
)
func (e *TitleStatusT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = TitleStatusT(s)
case string:
*e = TitleStatusT(s)
default:
return fmt.Errorf("unsupported scan type for TitleStatusT: %T", src)
}
return nil
}
type NullTitleStatusT struct {
TitleStatusT TitleStatusT `json:"title_status_t"`
Valid bool `json:"valid"` // Valid is true if TitleStatusT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullTitleStatusT) Scan(value interface{}) error {
if value == nil {
ns.TitleStatusT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.TitleStatusT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullTitleStatusT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.TitleStatusT), nil
}
type UsertitleStatusT string
const (
UsertitleStatusTFinished UsertitleStatusT = "finished"
UsertitleStatusTPlanned UsertitleStatusT = "planned"
UsertitleStatusTDropped UsertitleStatusT = "dropped"
UsertitleStatusTInProgress UsertitleStatusT = "in-progress"
)
func (e *UsertitleStatusT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = UsertitleStatusT(s)
case string:
*e = UsertitleStatusT(s)
default:
return fmt.Errorf("unsupported scan type for UsertitleStatusT: %T", src)
}
return nil
}
type NullUsertitleStatusT struct {
UsertitleStatusT UsertitleStatusT `json:"usertitle_status_t"`
Valid bool `json:"valid"` // Valid is true if UsertitleStatusT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullUsertitleStatusT) Scan(value interface{}) error {
if value == nil {
ns.UsertitleStatusT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.UsertitleStatusT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullUsertitleStatusT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.UsertitleStatusT), nil
}
type Images struct {
ImageID int32 `db:"image_id" json:"image_id"`
StorageType StorageTypeT `db:"storage_type" json:"storage_type"`
ImagePath string `db:"image_path" json:"image_path"`
}
type Providers struct {
ProviderID int32 `db:"provider_id" json:"provider_id"`
ProviderName string `db:"provider_name" json:"provider_name"`
}
type Reviews struct {
ReviewID int32 `db:"review_id" json:"review_id"`
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText string `db:"review_text" json:"review_text"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type Signals struct {
SignalID int32 `db:"signal_id" json:"signal_id"`
RawData []byte `db:"raw_data" json:"raw_data"`
ProviderID int32 `db:"provider_id" json:"provider_id"`
Dirty bool `db:"dirty" json:"dirty"`
}
type Studios struct {
StudioID int32 `db:"studio_id" json:"studio_id"`
StudioName pgtype.Text `db:"studio_name" json:"studio_name"`
IllustID pgtype.Int4 `db:"illust_id" json:"illust_id"`
StudioDesc pgtype.Text `db:"studio_desc" json:"studio_desc"`
}
type Tags struct {
TagID int32 `db:"tag_id" json:"tag_id"`
TagNames []byte `db:"tag_names" json:"tag_names"`
}
type TitleTags struct {
TitleID int32 `db:"title_id" json:"title_id"`
TagID int32 `db:"tag_id" json:"tag_id"`
}
type Titles struct {
TitleID int32 `db:"title_id" json:"title_id"`
TitleNames []byte `db:"title_names" json:"title_names"`
StudioID int32 `db:"studio_id" json:"studio_id"`
PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"`
SignalIds []int32 `db:"signal_ids" json:"signal_ids"`
TitleStatus TitleStatusT `db:"title_status" json:"title_status"`
Rating pgtype.Float8 `db:"rating" json:"rating"`
RatingCount pgtype.Int4 `db:"rating_count" json:"rating_count"`
ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"`
ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"`
Season pgtype.Int4 `db:"season" json:"season"`
EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"`
EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"`
EpisodesLen []byte `db:"episodes_len" json:"episodes_len"`
}
type Users struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Passhash string `db:"passhash" json:"passhash"`
Mail pgtype.Text `db:"mail" json:"mail"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type Usertitles struct {
UsertitleID int32 `db:"usertitle_id" json:"usertitle_id"`
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status UsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}

View file

@ -0,0 +1,712 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: query.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createImage = `-- name: CreateImage :one
INSERT INTO images (storage_type, image_path)
VALUES ($1, $2)
RETURNING image_id, storage_type, image_path
`
type CreateImageParams struct {
StorageType StorageTypeT `db:"storage_type" json:"storage_type"`
ImagePath string `db:"image_path" json:"image_path"`
}
func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Images, error) {
row := q.db.QueryRow(ctx, createImage, arg.StorageType, arg.ImagePath)
var i Images
err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath)
return i, err
}
const createReview = `-- name: CreateReview :one
INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date)
VALUES ($1, $2, $3, $4, $5)
RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date
`
type CreateReviewParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText string `db:"review_text" json:"review_text"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Reviews, error) {
row := q.db.QueryRow(ctx, createReview,
arg.UserID,
arg.TitleID,
arg.ImageIds,
arg.ReviewText,
arg.CreationDate,
)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const createUser = `-- name: CreateUser :one
INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date
`
type CreateUserParams struct {
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Passhash string `db:"passhash" json:"passhash"`
Mail pgtype.Text `db:"mail" json:"mail"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type CreateUserRow struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
row := q.db.QueryRow(ctx, createUser,
arg.AvatarID,
arg.Passhash,
arg.Mail,
arg.Nickname,
arg.DispName,
arg.UserDesc,
arg.CreationDate,
)
var i CreateUserRow
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const createUserTitle = `-- name: CreateUserTitle :one
INSERT INTO usertitles (user_id, title_id, status, rate, review_id)
VALUES ($1, $2, $3, $4, $5)
RETURNING usertitle_id, user_id, title_id, status, rate, review_id
`
type CreateUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status UsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}
func (q *Queries) CreateUserTitle(ctx context.Context, arg CreateUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, createUserTitle,
arg.UserID,
arg.TitleID,
arg.Status,
arg.Rate,
arg.ReviewID,
)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}
const deleteReview = `-- name: DeleteReview :exec
DELETE FROM reviews
WHERE review_id = $1
`
func (q *Queries) DeleteReview(ctx context.Context, reviewID int32) error {
_, err := q.db.Exec(ctx, deleteReview, reviewID)
return err
}
const deleteUser = `-- name: DeleteUser :exec
DELETE FROM users
WHERE user_id = $1
`
func (q *Queries) DeleteUser(ctx context.Context, userID int32) error {
_, err := q.db.Exec(ctx, deleteUser, userID)
return err
}
const deleteUserTitle = `-- name: DeleteUserTitle :exec
DELETE FROM usertitles
WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2)
`
type DeleteUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Column2 int32 `db:"column_2" json:"column_2"`
}
func (q *Queries) DeleteUserTitle(ctx context.Context, arg DeleteUserTitleParams) error {
_, err := q.db.Exec(ctx, deleteUserTitle, arg.UserID, arg.Column2)
return err
}
const getImageByID = `-- name: GetImageByID :one
SELECT image_id, storage_type, image_path
FROM images
WHERE image_id = $1
`
func (q *Queries) GetImageByID(ctx context.Context, imageID int32) (Images, error) {
row := q.db.QueryRow(ctx, getImageByID, imageID)
var i Images
err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath)
return i, err
}
const getReviewByID = `-- name: GetReviewByID :one
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE review_id = $1
`
func (q *Queries) GetReviewByID(ctx context.Context, reviewID int32) (Reviews, error) {
row := q.db.QueryRow(ctx, getReviewByID, reviewID)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const getTitleByID = `-- name: GetTitleByID :one
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
WHERE title_id = $1
`
func (q *Queries) GetTitleByID(ctx context.Context, titleID int32) (Titles, error) {
row := q.db.QueryRow(ctx, getTitleByID, titleID)
var i Titles
err := row.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
WHERE user_id = $1
`
func (q *Queries) GetUserByID(ctx context.Context, userID int32) (Users, error) {
row := q.db.QueryRow(ctx, getUserByID, userID)
var i Users
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Passhash,
&i.Mail,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const getUserTitle = `-- name: GetUserTitle :one
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1 AND title_id = $2
`
type GetUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
}
func (q *Queries) GetUserTitle(ctx context.Context, arg GetUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, getUserTitle, arg.UserID, arg.TitleID)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}
const listReviewsByTitle = `-- name: ListReviewsByTitle :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE title_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3
`
type ListReviewsByTitleParams struct {
TitleID int32 `db:"title_id" json:"title_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListReviewsByTitle(ctx context.Context, arg ListReviewsByTitleParams) ([]Reviews, error) {
rows, err := q.db.Query(ctx, listReviewsByTitle, arg.TitleID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Reviews
for rows.Next() {
var i Reviews
if err := rows.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listReviewsByUser = `-- name: ListReviewsByUser :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE user_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3
`
type ListReviewsByUserParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListReviewsByUser(ctx context.Context, arg ListReviewsByUserParams) ([]Reviews, error) {
rows, err := q.db.Query(ctx, listReviewsByUser, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Reviews
for rows.Next() {
var i Reviews
if err := rows.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listTags = `-- name: ListTags :many
SELECT tag_id, tag_names
FROM tags
ORDER BY tag_id
LIMIT $1 OFFSET $2
`
type ListTagsParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListTags(ctx context.Context, arg ListTagsParams) ([]Tags, error) {
rows, err := q.db.Query(ctx, listTags, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Tags
for rows.Next() {
var i Tags
if err := rows.Scan(&i.TagID, &i.TagNames); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listTitles = `-- name: ListTitles :many
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
ORDER BY title_id
LIMIT $1 OFFSET $2
`
type ListTitlesParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListTitles(ctx context.Context, arg ListTitlesParams) ([]Titles, error) {
rows, err := q.db.Query(ctx, listTitles, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Titles
for rows.Next() {
var i Titles
if err := rows.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listUserTitles = `-- name: ListUserTitles :many
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1
ORDER BY usertitle_id
LIMIT $2 OFFSET $3
`
type ListUserTitlesParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListUserTitles(ctx context.Context, arg ListUserTitlesParams) ([]Usertitles, error) {
rows, err := q.db.Query(ctx, listUserTitles, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Usertitles
for rows.Next() {
var i Usertitles
if err := rows.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listUsers = `-- name: ListUsers :many
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
ORDER BY user_id
LIMIT $1 OFFSET $2
`
type ListUsersParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]Users, error) {
rows, err := q.db.Query(ctx, listUsers, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Users
for rows.Next() {
var i Users
if err := rows.Scan(
&i.UserID,
&i.AvatarID,
&i.Passhash,
&i.Mail,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateReview = `-- name: UpdateReview :one
UPDATE reviews
SET
image_ids = COALESCE($1, image_ids),
review_text = COALESCE($2, review_text)
WHERE review_id = $3
RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date
`
type UpdateReviewParams struct {
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText pgtype.Text `db:"review_text" json:"review_text"`
ReviewID int32 `db:"review_id" json:"review_id"`
}
func (q *Queries) UpdateReview(ctx context.Context, arg UpdateReviewParams) (Reviews, error) {
row := q.db.QueryRow(ctx, updateReview, arg.ImageIds, arg.ReviewText, arg.ReviewID)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const updateTitle = `-- name: UpdateTitle :one
UPDATE titles
SET
title_names = COALESCE($1, title_names),
studio_id = COALESCE($2, studio_id),
poster_id = COALESCE($3, poster_id),
signal_ids = COALESCE($4, signal_ids),
title_status = COALESCE($5, title_status),
release_year = COALESCE($6, release_year),
release_season = COALESCE($7, release_season),
episodes_aired = COALESCE($8, episodes_aired),
episodes_all = COALESCE($9, episodes_all),
episodes_len = COALESCE($10, episodes_len)
WHERE title_id = $11
RETURNING title_id, title_names, studio_id, poster_id, signal_ids, title_status, rating, rating_count, release_year, release_season, season, episodes_aired, episodes_all, episodes_len
`
type UpdateTitleParams struct {
TitleNames []byte `db:"title_names" json:"title_names"`
StudioID pgtype.Int4 `db:"studio_id" json:"studio_id"`
PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"`
SignalIds []int32 `db:"signal_ids" json:"signal_ids"`
TitleStatus NullTitleStatusT `db:"title_status" json:"title_status"`
ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"`
ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"`
EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"`
EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"`
EpisodesLen []byte `db:"episodes_len" json:"episodes_len"`
TitleID int32 `db:"title_id" json:"title_id"`
}
func (q *Queries) UpdateTitle(ctx context.Context, arg UpdateTitleParams) (Titles, error) {
row := q.db.QueryRow(ctx, updateTitle,
arg.TitleNames,
arg.StudioID,
arg.PosterID,
arg.SignalIds,
arg.TitleStatus,
arg.ReleaseYear,
arg.ReleaseSeason,
arg.EpisodesAired,
arg.EpisodesAll,
arg.EpisodesLen,
arg.TitleID,
)
var i Titles
err := row.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
)
return i, err
}
const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
avatar_id = COALESCE($1, avatar_id),
disp_name = COALESCE($2, disp_name),
user_desc = COALESCE($3, user_desc)
WHERE user_id = $4
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date
`
type UpdateUserParams struct {
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
UserID int32 `db:"user_id" json:"user_id"`
}
type UpdateUserRow struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
row := q.db.QueryRow(ctx, updateUser,
arg.AvatarID,
arg.DispName,
arg.UserDesc,
arg.UserID,
)
var i UpdateUserRow
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const updateUserTitle = `-- name: UpdateUserTitle :one
UPDATE usertitles
SET
status = COALESCE($3, status),
rate = COALESCE($4, rate),
review_id = COALESCE($5, review_id)
WHERE user_id = $1 AND title_id = $2
RETURNING usertitle_id, user_id, title_id, status, rate, review_id
`
type UpdateUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status NullUsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}
func (q *Queries) UpdateUserTitle(ctx context.Context, arg UpdateUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, updateUserTitle,
arg.UserID,
arg.TitleID,
arg.Status,
arg.Rate,
arg.ReviewID,
)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}

View file

@ -3,6 +3,7 @@ module nyanimedb-server
go 1.25.0 go 1.25.0
require ( require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect github.com/cloudwego/base64x v0.1.6 // indirect
@ -15,12 +16,17 @@ require (
github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect github.com/goccy/go-yaml v1.18.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oapi-codegen/runtime v1.1.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect github.com/quic-go/quic-go v0.54.0 // indirect

View file

@ -1,3 +1,7 @@
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ=
github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA=
github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA=
@ -29,8 +33,17 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
@ -43,6 +56,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -52,6 +67,7 @@ github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQB
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=

View file

@ -1,35 +1,48 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"net/http" "nyanimedb-server/api"
"nyanimedb-server/db"
"os" "os"
"reflect" "reflect"
"time" "time"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5"
"github.com/pelletier/go-toml/v2" "github.com/pelletier/go-toml/v2"
log "github.com/sirupsen/logrus"
) )
var AppConfig Config var AppConfig Config
func main() { func main() {
if len(os.Args) != 2 { // if len(os.Args) != 2 {
AppConfig.Mode = "env" // AppConfig.Mode = "env"
} else { // } else {
AppConfig.Mode = "argv" // AppConfig.Mode = "argv"
// }
// err := InitConfig()
// if err != nil {
// log.Fatalf("Failed to init config: %v\n", err)
// }
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
} }
err := InitConfig() defer conn.Close(context.Background())
if err != nil {
log.Fatalf("Failed to init config: %v\n", err)
}
r := gin.Default() r := gin.Default()
r.LoadHTMLGlob("templates/*") queries := db.New(conn)
server := api.NewServer(queries)
// r.LoadHTMLGlob("templates/*")
r.Use(cors.New(cors.Config{ r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"}, // allow all origins, change to specific domains in production AllowOrigins: []string{"*"}, // allow all origins, change to specific domains in production
@ -40,22 +53,27 @@ func main() {
MaxAge: 12 * time.Hour, MaxAge: 12 * time.Hour,
})) }))
r.GET("/", func(c *gin.Context) { api.RegisterHandlers(r, api.NewStrictHandler(
c.HTML(http.StatusOK, "index.html", gin.H{ server,
"title": "Welcome Page", // сюда можно добавить middlewares, если нужно
"message": "Hello, Gin with HTML templates!", []api.StrictMiddlewareFunc{},
}) ))
}) // r.GET("/", func(c *gin.Context) {
// c.HTML(http.StatusOK, "index.html", gin.H{
// "title": "Welcome Page",
// "message": "Hello, Gin with HTML templates!",
// })
// })
r.GET("/api", func(c *gin.Context) { // r.GET("/api", func(c *gin.Context) {
items := []Item{ // items := []Item{
{ID: 1, Title: "First Item", Description: "This is the description of the first item."}, // {ID: 1, Title: "First Item", Description: "This is the description of the first item."},
{ID: 2, Title: "Second Item", Description: "This is the description of the second item."}, // {ID: 2, Title: "Second Item", Description: "This is the description of the second item."},
{ID: 3, Title: "Third Item", Description: "This is the description of the third item."}, // {ID: 3, Title: "Third Item", Description: "This is the description of the third item."},
} // }
c.JSON(http.StatusOK, items) // c.JSON(http.StatusOK, items)
}) // })
r.Run(":8080") r.Run(":8080")
} }

3
modules/sql/go.mod Normal file
View file

@ -0,0 +1,3 @@
module tutorial.sqlc.dev/app
go 1.25.2

142
modules/sql/query.sql Normal file
View file

@ -0,0 +1,142 @@
-- name: GetImageByID :one
SELECT image_id, storage_type, image_path
FROM images
WHERE image_id = $1;
-- name: CreateImage :one
INSERT INTO images (storage_type, image_path)
VALUES ($1, $2)
RETURNING image_id, storage_type, image_path;
-- name: GetUserByID :one
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
WHERE user_id = $1;
-- name: ListUsers :many
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
ORDER BY user_id
LIMIT $1 OFFSET $2;
-- name: CreateUser :one
INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
-- name: UpdateUser :one
UPDATE users
SET
avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id),
disp_name = COALESCE(sqlc.narg('disp_name'), disp_name),
user_desc = COALESCE(sqlc.narg('user_desc'), user_desc),
passhash = COALESCE(sqlc.narg('passhash'), passhash),
WHERE user_id = sqlc.arg('user_id')
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date;
-- name: DeleteUser :exec
DELETE FROM users
WHERE user_id = $1;
-- name: GetTitleByID :one
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
WHERE title_id = $1;
-- name: ListTitles :many
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
ORDER BY title_id
LIMIT $1 OFFSET $2;
-- name: UpdateTitle :one
UPDATE titles
SET
title_names = COALESCE(sqlc.narg('title_names'), title_names),
studio_id = COALESCE(sqlc.narg('studio_id'), studio_id),
poster_id = COALESCE(sqlc.narg('poster_id'), poster_id),
signal_ids = COALESCE(sqlc.narg('signal_ids'), signal_ids),
title_status = COALESCE(sqlc.narg('title_status'), title_status),
release_year = COALESCE(sqlc.narg('release_year'), release_year),
release_season = COALESCE(sqlc.narg('release_season'), release_season),
episodes_aired = COALESCE(sqlc.narg('episodes_aired'), episodes_aired),
episodes_all = COALESCE(sqlc.narg('episodes_all'), episodes_all),
episodes_len = COALESCE(sqlc.narg('episodes_len'), episodes_len)
WHERE title_id = sqlc.arg('title_id')
RETURNING *;
-- name: GetReviewByID :one
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE review_id = $1;
-- name: CreateReview :one
INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date)
VALUES ($1, $2, $3, $4, $5)
RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date;
-- name: UpdateReview :one
UPDATE reviews
SET
image_ids = COALESCE(sqlc.narg('image_ids'), image_ids),
review_text = COALESCE(sqlc.narg('review_text'), review_text)
WHERE review_id = sqlc.arg('review_id')
RETURNING *;
-- name: DeleteReview :exec
DELETE FROM reviews
WHERE review_id = $1;
-- name: ListReviewsByTitle :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE title_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3;
-- name: ListReviewsByUser :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE user_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3;
-- name: GetUserTitle :one
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1 AND title_id = $2;
-- name: ListUserTitles :many
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1
ORDER BY usertitle_id
LIMIT $2 OFFSET $3;
-- name: CreateUserTitle :one
INSERT INTO usertitles (user_id, title_id, status, rate, review_id)
VALUES ($1, $2, $3, $4, $5)
RETURNING usertitle_id, user_id, title_id, status, rate, review_id;
-- name: UpdateUserTitle :one
UPDATE usertitles
SET
status = COALESCE(sqlc.narg('status'), status),
rate = COALESCE(sqlc.narg('rate'), rate),
review_id = COALESCE(sqlc.narg('review_id'), review_id)
WHERE user_id = $1 AND title_id = $2
RETURNING *;
-- name: DeleteUserTitle :exec
DELETE FROM usertitles
WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2);
-- name: ListTags :many
SELECT tag_id, tag_names
FROM tags
ORDER BY tag_id
LIMIT $1 OFFSET $2;

97
modules/sql/scheme.sql Normal file
View file

@ -0,0 +1,97 @@
-- TODO:
-- title table triggers
-- maybe jsonb constraints
-- actions (delete)
BEGIN;
CREATE TYPE usertitle_status_t AS ENUM ('finished', 'planned', 'dropped', 'in-progress');
CREATE TYPE storage_type_t AS ENUM ('local', 's3');
CREATE TYPE title_status_t AS ENUM ('finished', 'ongoing', 'planned');
CREATE TYPE release_season_t AS ENUM ('winter', 'spring', 'summer', 'fall');
CREATE TABLE providers (
provider_id serial PRIMARY KEY,
provider_name varchar(64) NOT NULL
);
CREATE TABLE tags (
tag_id serial PRIMARY KEY,
tag_names jsonb NOT NULL --mb constraints
);
CREATE TABLE images (
image_id serial PRIMARY KEY,
storage_type storage_type_t NOT NULL,
image_path varchar(256) UNIQUE NOT NULL
);
CREATE TABLE users (
user_id serial PRIMARY KEY,
avatar_id int REFERENCES images (image_id),
passhash text NOT NULL,
mail varchar(64) CHECK (mail ~ '[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+'),
nickname varchar(16) NOT NULL CHECK (nickname ~ '^[a-zA-Z0-9_-]+$'),
disp_name varchar(32),
user_desc varchar(512),
creation_date timestamp NOT NULL
);
CREATE TABLE studios (
studio_id serial PRIMARY KEY,
studio_name varchar(64) UNIQUE,
illust_id int REFERENCES images (image_id),
studio_desc text
);
CREATE TABLE titles (
title_id serial PRIMARY KEY,
title_names jsonb NOT NULL,
studio_id int NOT NULL REFERENCES studios,
poster_id int REFERENCES images (image_id),
signal_ids int[] NOT NULL,
title_status title_status_t NOT NULL,
rating float CHECK (rating > 0 AND rating <= 10), --by trigger
rating_count int CHECK (rating_count >= 0), --by trigger
release_year int CHECK (release_year >= 1900),
release_season release_season_t,
season int CHECK (season >= 0),
episodes_aired int CHECK (episodes_aired >= 0),
episodes_all int CHECK (episodes_all >= 0),
episodes_len jsonb,
CHECK ((episodes_aired IS NULL AND episodes_all IS NULL)
OR (episodes_aired IS NOT NULL AND episodes_all IS NOT NULL
AND episodes_aired <= episodes_all))
);
CREATE TABLE reviews (
review_id serial PRIMARY KEY,
user_id int NOT NULL REFERENCES users,
title_id int NOT NULL REFERENCES titles,
image_ids int[],
review_text text NOT NULL,
creation_date timestamp NOT NULL
);
CREATE TABLE usertitles (
usertitle_id serial PRIMARY KEY,
user_id int NOT NULL REFERENCES users,
title_id int NOT NULL REFERENCES titles,
status usertitle_status_t NOT NULL,
rate int CHECK (rate > 0 AND rate <= 10),
review_id int REFERENCES reviews
);
CREATE TABLE title_tags (
PRIMARY KEY (title_id, tag_id),
title_id int NOT NULL REFERENCES titles,
tag_id int NOT NULL REFERENCES tags
);
CREATE TABLE signals (
signal_id serial PRIMARY KEY,
raw_data jsonb NOT NULL,
provider_id int NOT NULL REFERENCES providers,
dirty bool NOT NULL
);
COMMIT;

View file

@ -0,0 +1,13 @@
version: "2"
sql:
- engine: "postgresql"
queries: "query.sql"
schema: "scheme.sql"
gen:
go:
package: "nyanimedb.backend"
out: "sqlc"
sql_package: "pgx/v5"
sql_driver: "github.com/lib/pq"
emit_db_tags: true
emit_exact_table_names: true

14
modules/sql/sqlc.yaml Normal file
View file

@ -0,0 +1,14 @@
version: "2"
sql:
- engine: "postgresql"
queries: "query.sql"
schema: "scheme.sql"
gen:
go:
package: "nyanimedb_backend"
out: "sqlc"
sql_package: "pgx/v5"
sql_driver: "github.com/lib/pq"
emit_db_tags: true
emit_exact_table_names: true
emit_json_tags: true

32
modules/sql/sqlc/db.go Normal file
View file

@ -0,0 +1,32 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package nyanimedb_backend
import (
"context"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
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
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
return &Queries{
db: tx,
}
}

266
modules/sql/sqlc/models.go Normal file
View file

@ -0,0 +1,266 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
package nyanimedb_backend
import (
"database/sql/driver"
"fmt"
"github.com/jackc/pgx/v5/pgtype"
)
type ReleaseSeasonT string
const (
ReleaseSeasonTWinter ReleaseSeasonT = "winter"
ReleaseSeasonTSpring ReleaseSeasonT = "spring"
ReleaseSeasonTSummer ReleaseSeasonT = "summer"
ReleaseSeasonTFall ReleaseSeasonT = "fall"
)
func (e *ReleaseSeasonT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = ReleaseSeasonT(s)
case string:
*e = ReleaseSeasonT(s)
default:
return fmt.Errorf("unsupported scan type for ReleaseSeasonT: %T", src)
}
return nil
}
type NullReleaseSeasonT struct {
ReleaseSeasonT ReleaseSeasonT `json:"release_season_t"`
Valid bool `json:"valid"` // Valid is true if ReleaseSeasonT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullReleaseSeasonT) Scan(value interface{}) error {
if value == nil {
ns.ReleaseSeasonT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.ReleaseSeasonT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullReleaseSeasonT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.ReleaseSeasonT), nil
}
type StorageTypeT string
const (
StorageTypeTLocal StorageTypeT = "local"
StorageTypeTS3 StorageTypeT = "s3"
)
func (e *StorageTypeT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = StorageTypeT(s)
case string:
*e = StorageTypeT(s)
default:
return fmt.Errorf("unsupported scan type for StorageTypeT: %T", src)
}
return nil
}
type NullStorageTypeT struct {
StorageTypeT StorageTypeT `json:"storage_type_t"`
Valid bool `json:"valid"` // Valid is true if StorageTypeT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullStorageTypeT) Scan(value interface{}) error {
if value == nil {
ns.StorageTypeT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.StorageTypeT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullStorageTypeT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.StorageTypeT), nil
}
type TitleStatusT string
const (
TitleStatusTFinished TitleStatusT = "finished"
TitleStatusTOngoing TitleStatusT = "ongoing"
TitleStatusTPlanned TitleStatusT = "planned"
)
func (e *TitleStatusT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = TitleStatusT(s)
case string:
*e = TitleStatusT(s)
default:
return fmt.Errorf("unsupported scan type for TitleStatusT: %T", src)
}
return nil
}
type NullTitleStatusT struct {
TitleStatusT TitleStatusT `json:"title_status_t"`
Valid bool `json:"valid"` // Valid is true if TitleStatusT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullTitleStatusT) Scan(value interface{}) error {
if value == nil {
ns.TitleStatusT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.TitleStatusT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullTitleStatusT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.TitleStatusT), nil
}
type UsertitleStatusT string
const (
UsertitleStatusTFinished UsertitleStatusT = "finished"
UsertitleStatusTPlanned UsertitleStatusT = "planned"
UsertitleStatusTDropped UsertitleStatusT = "dropped"
UsertitleStatusTInProgress UsertitleStatusT = "in-progress"
)
func (e *UsertitleStatusT) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = UsertitleStatusT(s)
case string:
*e = UsertitleStatusT(s)
default:
return fmt.Errorf("unsupported scan type for UsertitleStatusT: %T", src)
}
return nil
}
type NullUsertitleStatusT struct {
UsertitleStatusT UsertitleStatusT `json:"usertitle_status_t"`
Valid bool `json:"valid"` // Valid is true if UsertitleStatusT is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullUsertitleStatusT) Scan(value interface{}) error {
if value == nil {
ns.UsertitleStatusT, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.UsertitleStatusT.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullUsertitleStatusT) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.UsertitleStatusT), nil
}
type Images struct {
ImageID int32 `db:"image_id" json:"image_id"`
StorageType StorageTypeT `db:"storage_type" json:"storage_type"`
ImagePath string `db:"image_path" json:"image_path"`
}
type Providers struct {
ProviderID int32 `db:"provider_id" json:"provider_id"`
ProviderName string `db:"provider_name" json:"provider_name"`
}
type Reviews struct {
ReviewID int32 `db:"review_id" json:"review_id"`
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText string `db:"review_text" json:"review_text"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type Signals struct {
SignalID int32 `db:"signal_id" json:"signal_id"`
RawData []byte `db:"raw_data" json:"raw_data"`
ProviderID int32 `db:"provider_id" json:"provider_id"`
Dirty bool `db:"dirty" json:"dirty"`
}
type Studios struct {
StudioID int32 `db:"studio_id" json:"studio_id"`
StudioName pgtype.Text `db:"studio_name" json:"studio_name"`
IllustID pgtype.Int4 `db:"illust_id" json:"illust_id"`
StudioDesc pgtype.Text `db:"studio_desc" json:"studio_desc"`
}
type Tags struct {
TagID int32 `db:"tag_id" json:"tag_id"`
TagNames []byte `db:"tag_names" json:"tag_names"`
}
type TitleTags struct {
TitleID int32 `db:"title_id" json:"title_id"`
TagID int32 `db:"tag_id" json:"tag_id"`
}
type Titles struct {
TitleID int32 `db:"title_id" json:"title_id"`
TitleNames []byte `db:"title_names" json:"title_names"`
StudioID int32 `db:"studio_id" json:"studio_id"`
PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"`
SignalIds []int32 `db:"signal_ids" json:"signal_ids"`
TitleStatus TitleStatusT `db:"title_status" json:"title_status"`
Rating pgtype.Float8 `db:"rating" json:"rating"`
RatingCount pgtype.Int4 `db:"rating_count" json:"rating_count"`
ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"`
ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"`
Season pgtype.Int4 `db:"season" json:"season"`
EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"`
EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"`
EpisodesLen []byte `db:"episodes_len" json:"episodes_len"`
}
type Users struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Passhash string `db:"passhash" json:"passhash"`
Mail pgtype.Text `db:"mail" json:"mail"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type Usertitles struct {
UsertitleID int32 `db:"usertitle_id" json:"usertitle_id"`
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status UsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}

View file

@ -0,0 +1,712 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.30.0
// source: query.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createImage = `-- name: CreateImage :one
INSERT INTO images (storage_type, image_path)
VALUES ($1, $2)
RETURNING image_id, storage_type, image_path
`
type CreateImageParams struct {
StorageType StorageTypeT `db:"storage_type" json:"storage_type"`
ImagePath string `db:"image_path" json:"image_path"`
}
func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Images, error) {
row := q.db.QueryRow(ctx, createImage, arg.StorageType, arg.ImagePath)
var i Images
err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath)
return i, err
}
const createReview = `-- name: CreateReview :one
INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date)
VALUES ($1, $2, $3, $4, $5)
RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date
`
type CreateReviewParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText string `db:"review_text" json:"review_text"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Reviews, error) {
row := q.db.QueryRow(ctx, createReview,
arg.UserID,
arg.TitleID,
arg.ImageIds,
arg.ReviewText,
arg.CreationDate,
)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const createUser = `-- name: CreateUser :one
INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date
`
type CreateUserParams struct {
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Passhash string `db:"passhash" json:"passhash"`
Mail pgtype.Text `db:"mail" json:"mail"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
type CreateUserRow struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
row := q.db.QueryRow(ctx, createUser,
arg.AvatarID,
arg.Passhash,
arg.Mail,
arg.Nickname,
arg.DispName,
arg.UserDesc,
arg.CreationDate,
)
var i CreateUserRow
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const createUserTitle = `-- name: CreateUserTitle :one
INSERT INTO usertitles (user_id, title_id, status, rate, review_id)
VALUES ($1, $2, $3, $4, $5)
RETURNING usertitle_id, user_id, title_id, status, rate, review_id
`
type CreateUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status UsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}
func (q *Queries) CreateUserTitle(ctx context.Context, arg CreateUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, createUserTitle,
arg.UserID,
arg.TitleID,
arg.Status,
arg.Rate,
arg.ReviewID,
)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}
const deleteReview = `-- name: DeleteReview :exec
DELETE FROM reviews
WHERE review_id = $1
`
func (q *Queries) DeleteReview(ctx context.Context, reviewID int32) error {
_, err := q.db.Exec(ctx, deleteReview, reviewID)
return err
}
const deleteUser = `-- name: DeleteUser :exec
DELETE FROM users
WHERE user_id = $1
`
func (q *Queries) DeleteUser(ctx context.Context, userID int32) error {
_, err := q.db.Exec(ctx, deleteUser, userID)
return err
}
const deleteUserTitle = `-- name: DeleteUserTitle :exec
DELETE FROM usertitles
WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2)
`
type DeleteUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Column2 int32 `db:"column_2" json:"column_2"`
}
func (q *Queries) DeleteUserTitle(ctx context.Context, arg DeleteUserTitleParams) error {
_, err := q.db.Exec(ctx, deleteUserTitle, arg.UserID, arg.Column2)
return err
}
const getImageByID = `-- name: GetImageByID :one
SELECT image_id, storage_type, image_path
FROM images
WHERE image_id = $1
`
func (q *Queries) GetImageByID(ctx context.Context, imageID int32) (Images, error) {
row := q.db.QueryRow(ctx, getImageByID, imageID)
var i Images
err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath)
return i, err
}
const getReviewByID = `-- name: GetReviewByID :one
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE review_id = $1
`
func (q *Queries) GetReviewByID(ctx context.Context, reviewID int32) (Reviews, error) {
row := q.db.QueryRow(ctx, getReviewByID, reviewID)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const getTitleByID = `-- name: GetTitleByID :one
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
WHERE title_id = $1
`
func (q *Queries) GetTitleByID(ctx context.Context, titleID int32) (Titles, error) {
row := q.db.QueryRow(ctx, getTitleByID, titleID)
var i Titles
err := row.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
)
return i, err
}
const getUserByID = `-- name: GetUserByID :one
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
WHERE user_id = $1
`
func (q *Queries) GetUserByID(ctx context.Context, userID int32) (Users, error) {
row := q.db.QueryRow(ctx, getUserByID, userID)
var i Users
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Passhash,
&i.Mail,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const getUserTitle = `-- name: GetUserTitle :one
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1 AND title_id = $2
`
type GetUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
}
func (q *Queries) GetUserTitle(ctx context.Context, arg GetUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, getUserTitle, arg.UserID, arg.TitleID)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}
const listReviewsByTitle = `-- name: ListReviewsByTitle :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE title_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3
`
type ListReviewsByTitleParams struct {
TitleID int32 `db:"title_id" json:"title_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListReviewsByTitle(ctx context.Context, arg ListReviewsByTitleParams) ([]Reviews, error) {
rows, err := q.db.Query(ctx, listReviewsByTitle, arg.TitleID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Reviews
for rows.Next() {
var i Reviews
if err := rows.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listReviewsByUser = `-- name: ListReviewsByUser :many
SELECT review_id, user_id, title_id, image_ids, review_text, creation_date
FROM reviews
WHERE user_id = $1
ORDER BY creation_date DESC
LIMIT $2 OFFSET $3
`
type ListReviewsByUserParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListReviewsByUser(ctx context.Context, arg ListReviewsByUserParams) ([]Reviews, error) {
rows, err := q.db.Query(ctx, listReviewsByUser, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Reviews
for rows.Next() {
var i Reviews
if err := rows.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listTags = `-- name: ListTags :many
SELECT tag_id, tag_names
FROM tags
ORDER BY tag_id
LIMIT $1 OFFSET $2
`
type ListTagsParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListTags(ctx context.Context, arg ListTagsParams) ([]Tags, error) {
rows, err := q.db.Query(ctx, listTags, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Tags
for rows.Next() {
var i Tags
if err := rows.Scan(&i.TagID, &i.TagNames); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listTitles = `-- name: ListTitles :many
SELECT title_id, title_names, studio_id, poster_id, signal_ids,
title_status, rating, rating_count, release_year, release_season,
season, episodes_aired, episodes_all, episodes_len
FROM titles
ORDER BY title_id
LIMIT $1 OFFSET $2
`
type ListTitlesParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListTitles(ctx context.Context, arg ListTitlesParams) ([]Titles, error) {
rows, err := q.db.Query(ctx, listTitles, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Titles
for rows.Next() {
var i Titles
if err := rows.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listUserTitles = `-- name: ListUserTitles :many
SELECT usertitle_id, user_id, title_id, status, rate, review_id
FROM usertitles
WHERE user_id = $1
ORDER BY usertitle_id
LIMIT $2 OFFSET $3
`
type ListUserTitlesParams struct {
UserID int32 `db:"user_id" json:"user_id"`
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListUserTitles(ctx context.Context, arg ListUserTitlesParams) ([]Usertitles, error) {
rows, err := q.db.Query(ctx, listUserTitles, arg.UserID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Usertitles
for rows.Next() {
var i Usertitles
if err := rows.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listUsers = `-- name: ListUsers :many
SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date
FROM users
ORDER BY user_id
LIMIT $1 OFFSET $2
`
type ListUsersParams struct {
Limit int32 `db:"limit" json:"limit"`
Offset int32 `db:"offset" json:"offset"`
}
func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]Users, error) {
rows, err := q.db.Query(ctx, listUsers, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Users
for rows.Next() {
var i Users
if err := rows.Scan(
&i.UserID,
&i.AvatarID,
&i.Passhash,
&i.Mail,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateReview = `-- name: UpdateReview :one
UPDATE reviews
SET
image_ids = COALESCE($1, image_ids),
review_text = COALESCE($2, review_text)
WHERE review_id = $3
RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date
`
type UpdateReviewParams struct {
ImageIds []int32 `db:"image_ids" json:"image_ids"`
ReviewText pgtype.Text `db:"review_text" json:"review_text"`
ReviewID int32 `db:"review_id" json:"review_id"`
}
func (q *Queries) UpdateReview(ctx context.Context, arg UpdateReviewParams) (Reviews, error) {
row := q.db.QueryRow(ctx, updateReview, arg.ImageIds, arg.ReviewText, arg.ReviewID)
var i Reviews
err := row.Scan(
&i.ReviewID,
&i.UserID,
&i.TitleID,
&i.ImageIds,
&i.ReviewText,
&i.CreationDate,
)
return i, err
}
const updateTitle = `-- name: UpdateTitle :one
UPDATE titles
SET
title_names = COALESCE($1, title_names),
studio_id = COALESCE($2, studio_id),
poster_id = COALESCE($3, poster_id),
signal_ids = COALESCE($4, signal_ids),
title_status = COALESCE($5, title_status),
release_year = COALESCE($6, release_year),
release_season = COALESCE($7, release_season),
episodes_aired = COALESCE($8, episodes_aired),
episodes_all = COALESCE($9, episodes_all),
episodes_len = COALESCE($10, episodes_len)
WHERE title_id = $11
RETURNING title_id, title_names, studio_id, poster_id, signal_ids, title_status, rating, rating_count, release_year, release_season, season, episodes_aired, episodes_all, episodes_len
`
type UpdateTitleParams struct {
TitleNames []byte `db:"title_names" json:"title_names"`
StudioID pgtype.Int4 `db:"studio_id" json:"studio_id"`
PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"`
SignalIds []int32 `db:"signal_ids" json:"signal_ids"`
TitleStatus NullTitleStatusT `db:"title_status" json:"title_status"`
ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"`
ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"`
EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"`
EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"`
EpisodesLen []byte `db:"episodes_len" json:"episodes_len"`
TitleID int32 `db:"title_id" json:"title_id"`
}
func (q *Queries) UpdateTitle(ctx context.Context, arg UpdateTitleParams) (Titles, error) {
row := q.db.QueryRow(ctx, updateTitle,
arg.TitleNames,
arg.StudioID,
arg.PosterID,
arg.SignalIds,
arg.TitleStatus,
arg.ReleaseYear,
arg.ReleaseSeason,
arg.EpisodesAired,
arg.EpisodesAll,
arg.EpisodesLen,
arg.TitleID,
)
var i Titles
err := row.Scan(
&i.TitleID,
&i.TitleNames,
&i.StudioID,
&i.PosterID,
&i.SignalIds,
&i.TitleStatus,
&i.Rating,
&i.RatingCount,
&i.ReleaseYear,
&i.ReleaseSeason,
&i.Season,
&i.EpisodesAired,
&i.EpisodesAll,
&i.EpisodesLen,
)
return i, err
}
const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
avatar_id = COALESCE($1, avatar_id),
disp_name = COALESCE($2, disp_name),
user_desc = COALESCE($3, user_desc)
WHERE user_id = $4
RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date
`
type UpdateUserParams struct {
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
UserID int32 `db:"user_id" json:"user_id"`
}
type UpdateUserRow struct {
UserID int32 `db:"user_id" json:"user_id"`
AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"`
Nickname string `db:"nickname" json:"nickname"`
DispName pgtype.Text `db:"disp_name" json:"disp_name"`
UserDesc pgtype.Text `db:"user_desc" json:"user_desc"`
CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
row := q.db.QueryRow(ctx, updateUser,
arg.AvatarID,
arg.DispName,
arg.UserDesc,
arg.UserID,
)
var i UpdateUserRow
err := row.Scan(
&i.UserID,
&i.AvatarID,
&i.Nickname,
&i.DispName,
&i.UserDesc,
&i.CreationDate,
)
return i, err
}
const updateUserTitle = `-- name: UpdateUserTitle :one
UPDATE usertitles
SET
status = COALESCE($3, status),
rate = COALESCE($4, rate),
review_id = COALESCE($5, review_id)
WHERE user_id = $1 AND title_id = $2
RETURNING usertitle_id, user_id, title_id, status, rate, review_id
`
type UpdateUserTitleParams struct {
UserID int32 `db:"user_id" json:"user_id"`
TitleID int32 `db:"title_id" json:"title_id"`
Status NullUsertitleStatusT `db:"status" json:"status"`
Rate pgtype.Int4 `db:"rate" json:"rate"`
ReviewID pgtype.Int4 `db:"review_id" json:"review_id"`
}
func (q *Queries) UpdateUserTitle(ctx context.Context, arg UpdateUserTitleParams) (Usertitles, error) {
row := q.db.QueryRow(ctx, updateUserTitle,
arg.UserID,
arg.TitleID,
arg.Status,
arg.Rate,
arg.ReviewID,
)
var i Usertitles
err := row.Scan(
&i.UsertitleID,
&i.UserID,
&i.TitleID,
&i.Status,
&i.Rate,
&i.ReviewID,
)
return i, err
}