pkgdash/internal/storage/sqlite/storage.go

322 lines
6.3 KiB
Go
Raw Normal View History

package sqlite
import (
"context"
"database/sql"
"embed"
"errors"
"fmt"
"strings"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/sqlite"
"github.com/golang-migrate/migrate/v4/source/iofs"
"github.com/lib/pq"
_ "github.com/mattn/go-sqlite3"
"go.unistack.org/micro/v4/logger"
"git.unistack.org/unistack-org/pkgdash/internal/models"
pb "git.unistack.org/unistack-org/pkgdash/proto"
)
const (
pathMigration = `migrations/sqlite`
)
type Sqlite struct {
db *sql.DB
fs embed.FS
}
2023-08-13 00:09:57 +03:00
func NewStorage() func(*sql.DB, embed.FS) interface{} {
return func(db *sql.DB, fs embed.FS) interface{} {
return &Sqlite{db: db, fs: fs}
}
}
func (s *Sqlite) MigrateUp() error {
driver, err := sqlite.WithInstance(s.db, &sqlite.Config{
MigrationsTable: sqlite.DefaultMigrationsTable,
DatabaseName: "pkgdash",
})
if err != nil {
return err
}
source, err := iofs.New(s.fs, pathMigration)
if err != nil {
return err
}
// TODO: pass own logger
m, err := migrate.NewWithInstance("fs", source, "pkgdash", driver)
if err != nil {
return err
}
if err = m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
return err
}
return nil
}
func (s *Sqlite) MigrateDown() error {
driver, err := sqlite.WithInstance(s.db, &sqlite.Config{
MigrationsTable: sqlite.DefaultMigrationsTable,
DatabaseName: "pkgdash",
})
if err != nil {
return err
}
source, err := iofs.New(s.fs, pathMigration)
if err != nil {
return err
}
// TODO: pass own logger
m, err := migrate.NewWithInstance("fs", source, "pkgdash", driver)
if err != nil {
return err
}
if err = m.Down(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
return err
}
return nil
}
func (s *Sqlite) PackagesUpdate(ctx context.Context, req *pb.PackagesUpdateReq) error {
panic("need implement")
}
func (s *Sqlite) PackagesList(ctx context.Context, req *pb.PackagesListReq) ([]*models.Package, error) {
var packages []*models.Package
rows, err := s.db.QueryContext(ctx, queryPackagesList)
if err != nil {
return nil, err
}
for ; rows.Err() == nil; rows.Next() {
pkg := &models.Package{}
if err = rows.Scan(
&pkg.ID,
&pkg.Name,
&pkg.URL,
&pkg.Comments,
); err != nil {
_ = rows.Close()
return nil, err
}
packages = append(packages, pkg)
}
if err = rows.Err(); err != nil {
return nil, err
}
if err = rows.Close(); err != nil {
return nil, err
}
return packages, err
}
func (s *Sqlite) CommentsCreate(ctx context.Context, req *pb.CommentsCreateReq) (id uint64, err error) {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
2023-08-12 19:26:50 +03:00
return 0, err
}
defer func() {
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
logger.Errorf(ctx, "AddComment: unable to rollback: %v", rollbackErr)
}
} else {
err = tx.Commit()
}
}()
if err = tx.QueryRowContext(ctx, queryCommentsCreate, req.Text, req.PackageId).Scan(&id); err != nil {
2023-08-12 19:26:50 +03:00
return id, err
}
2023-08-13 00:09:57 +03:00
2023-08-12 19:26:50 +03:00
return id, err
}
func (s *Sqlite) PackagesCreate(ctx context.Context, req *pb.PackagesCreateReq) error {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer func() {
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
logger.Errorf(ctx, "AddPackage: unable to rollback: %v", rollbackErr)
}
} else {
err = tx.Commit()
}
}()
res, err := tx.ExecContext(ctx, queryPackagesCreate, req.Name, req.Url, pq.Array(req.Modules))
if err != nil {
return err
}
if aff, affErr := res.RowsAffected(); err != nil {
err = affErr
} else if aff == 0 {
err = errors.New("rows affected is 0")
}
return err
}
func (s *Sqlite) InsertButchModules(ctx context.Context, req []models.Module) ([]uint64, error) {
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
logger.Errorf(ctx, "AddPackage: unable to rollback: %v", rollbackErr)
}
} else {
err = tx.Commit()
}
}()
query := generateQuery(req)
rows, err := tx.QueryContext(ctx, query)
if err != nil {
return nil, err
}
defer func() {
if err = rows.Close(); err != nil {
return
}
err = rows.Err()
}()
result := make([]uint64, 0)
for rows.Next() {
tmp := uint64(0)
if err = rows.Scan(&tmp); err != nil {
return nil, err
}
result = append(result, tmp)
}
return result, err
}
func (s *Sqlite) GetModule(ctx context.Context, req *pb.ModulesListReq) ([]*models.Module, error) {
var err error
var modules []*models.Module
rows, err := s.db.QueryContext(ctx, queryModulesList)
if err != nil {
return nil, err
}
defer func() {
if err = rows.Close(); err != nil {
return
}
err = rows.Err()
}()
for ; rows.Err() == nil; rows.Next() {
mod := &models.Module{}
if err = rows.Scan(
&mod.ID,
&mod.Name,
&mod.Version,
&mod.LastVersion,
); err != nil {
return nil, err
}
modules = append(modules, mod)
}
if err = rows.Err(); err != nil {
return nil, err
}
if err = rows.Close(); err != nil {
return nil, err
}
return modules, nil
}
func (s *Sqlite) CommentsList(ctx context.Context, req *pb.CommentsListReq) ([]*models.Comment, error) {
var comments []*models.Comment
2023-08-14 14:27:29 +03:00
rows, err := s.db.QueryContext(ctx, queryCommentsList, req.PackageId)
2023-08-14 14:27:29 +03:00
if err != nil {
return nil, err
}
defer func() {
if err = rows.Close(); err != nil {
return
}
err = rows.Err()
}()
for ; rows.Err() == nil; rows.Next() {
com := &models.Comment{}
2023-08-14 14:27:29 +03:00
if err = rows.Scan(
&com.ID,
&com.Text,
&com.Created,
&com.Updated,
2023-08-14 14:27:29 +03:00
); err != nil {
_ = rows.Close()
2023-08-14 14:27:29 +03:00
return nil, err
}
comments = append(comments, com)
2023-08-14 14:27:29 +03:00
}
if err = rows.Err(); err != nil {
return nil, err
}
if err = rows.Close(); err != nil {
return nil, err
}
return comments, nil
2023-08-14 14:27:29 +03:00
}
func convertSliceUInt(arg ...uint64) []interface{} {
result := make([]interface{}, 0, len(arg))
for i := range arg {
result = append(result, arg[i])
}
return result
}
func generateQuery(rsp []models.Module) string {
const pattern = `%c('%s', '%s', '%s')`
build := strings.Builder{}
comma := ' '
for i := range rsp {
str := fmt.Sprintf(pattern, comma, rsp[i].Name, rsp[i].Version, rsp[i].LastVersion)
build.WriteString(str)
comma = ','
}
return fmt.Sprintf(queryInsMsgGetIDs, build.String())
}
func generateArrayIneq(count int) string {
return "(?" + strings.Repeat(",?", count-1) + ")"
}