package sqlite import ( "database/sql" _ "embed" "fmt" "time" "git.arav.su/Arav/dwelling-home/pkg/mindflow" "github.com/pkg/errors" ) var ( //go:embed queries/schema.sql querySchema string //go:embed queries/categoryGetAll.sql queryCategoryGetAll string //go:embed queries/categoryNew.sql queryCategoryNew string //go:embed queries/categoryEdit.sql queryCategoryEdit string //go:embed queries/categoryDelete.sql queryCategoryDelete string //go:embed queries/postGetAll.sql queryPostGetAll string //go:embed queries/postNew.sql queryPostNew string //go:embed queries/postEdit.sql queryPostEdit string //go:embed queries/postDelete.sql queryPostDelete string ) var ( stmtCategoryGetAll *sql.Stmt stmtCategoryNew *sql.Stmt stmtCategoryEdit *sql.Stmt stmtCategoryDelete *sql.Stmt stmtPostGetAll *sql.Stmt stmtPostNew *sql.Stmt stmtPostEdit *sql.Stmt stmtPostDelete *sql.Stmt ) func initDBStatements(db *sql.DB) error { db.Exec("PRAGMA foreign_keys = ON;") _, err := db.Exec(querySchema) if err != nil { return errors.Wrap(err, "failed to init schema") } stmtCategoryGetAll, err = db.Prepare(queryCategoryGetAll) if err != nil { return errors.Wrap(err, "failed to prepare queryCategoryGetAll") } stmtCategoryNew, err = db.Prepare(queryCategoryNew) if err != nil { return errors.Wrap(err, "failed to prepare queryCategoryNew") } stmtCategoryEdit, err = db.Prepare(queryCategoryEdit) if err != nil { return errors.Wrap(err, "failed to prepare queryCategoryEdit") } stmtCategoryDelete, err = db.Prepare(queryCategoryDelete) if err != nil { return errors.Wrap(err, "failed to prepare queryCategoryDelete") } stmtPostGetAll, err = db.Prepare(queryPostGetAll) if err != nil { return errors.Wrap(err, "failed to prepare queryPostGetAll") } stmtPostNew, err = db.Prepare(queryPostNew) if err != nil { return errors.Wrap(err, "failed to prepare queryPostNew") } stmtPostEdit, err = db.Prepare(queryPostEdit) if err != nil { return errors.Wrap(err, "failed to prepare queryPostEdit") } stmtPostDelete, err = db.Prepare(queryPostDelete) if err != nil { return errors.Wrap(err, "failed to prepare queryPostDelete") } return nil } type SQLiteMindflow struct { db *sql.DB } func New(path string) (mindflow.Mindflow, error) { db, err := sql.Open("sqlite3", dsn(path)) if err != nil { return nil, err } if err := initDBStatements(db); err != nil { return nil, err } return &SQLiteMindflow{db: db}, nil } func dsn(filePath string) string { return fmt.Sprintf("file:%s?_journal=WAL&_mutex=full", filePath) } func (s *SQLiteMindflow) NewPost(post *mindflow.Post) error { tx, err := s.db.Begin() if err != nil { return err } defer tx.Rollback() r, err := tx.Stmt(stmtPostNew).Exec(post.Category.ID, post.Date.UTC().Unix(), post.Title, post.URL, post.Body) if err != nil { return err } post.ID, err = r.LastInsertId() if err != nil { return err } tx.Commit() return nil } func (s *SQLiteMindflow) EditPost(post *mindflow.Post) error { tx, err := s.db.Begin() if err != nil { return err } defer tx.Rollback() _, err = tx.Stmt(stmtPostEdit).Exec(post.Category.ID, post.Title, post.URL, post.Body, post.ID) if err != nil { return err } tx.Commit() return nil } func (s *SQLiteMindflow) DeletePost(id int64) error { tx, err := s.db.Begin() if err != nil { return err } defer tx.Rollback() if _, err = tx.Stmt(stmtPostDelete).Exec(id); err != nil { return err } tx.Commit() return nil } func (s *SQLiteMindflow) Posts() (posts []mindflow.Post, err error) { tx, err := s.db.Begin() if err != nil { return nil, err } defer tx.Rollback() rows, err := tx.Stmt(stmtPostGetAll).Query() if err != nil { return nil, err } defer rows.Close() for rows.Next() { var post mindflow.Post var date_unix int64 if err = rows.Scan(&post.ID, &post.Category.ID, &post.Category.Name, &date_unix, &post.Title, &post.URL, &post.Body); err != nil { return nil, err } post.Date = time.Unix(date_unix, 0) posts = append(posts, post) } tx.Commit() return posts, nil } func (s *SQLiteMindflow) NewCategory(category *mindflow.Category) (int64, error) { tx, err := s.db.Begin() if err != nil { return 0, err } defer tx.Rollback() r, err := tx.Stmt(stmtCategoryNew).Exec(category.Name) if err != nil { return 0, err } id, err := r.LastInsertId() if err != nil { return 0, err } tx.Commit() return id, nil } func (s *SQLiteMindflow) EditCategory(category *mindflow.Category) error { tx, err := s.db.Begin() if err != nil { return err } defer tx.Rollback() _, err = tx.Stmt(stmtCategoryEdit).Exec(category.Name, category.ID) if err != nil { return err } tx.Commit() return nil } func (s *SQLiteMindflow) DeleteCategory(id int64) (err error) { tx, err := s.db.Begin() if err != nil { return err } defer tx.Rollback() if _, err = tx.Stmt(stmtCategoryDelete).Exec(id); err != nil { return err } tx.Commit() return nil } func (s *SQLiteMindflow) Categories() (categories []mindflow.Category, err error) { tx, err := s.db.Begin() if err != nil { return nil, err } defer tx.Rollback() rows, err := tx.Stmt(stmtCategoryGetAll).Query() if err != nil { return nil, err } defer rows.Close() for rows.Next() { var category mindflow.Category if err = rows.Scan(&category.ID, &category.Name); err != nil { return nil, err } categories = append(categories, category) } tx.Commit() return categories, nil } func (s *SQLiteMindflow) Close() error { stmtCategoryGetAll.Close() stmtCategoryNew.Close() stmtCategoryEdit.Close() stmtCategoryDelete.Close() stmtPostDelete.Close() stmtPostEdit.Close() stmtPostGetAll.Close() stmtPostNew.Close() return s.db.Close() }