package sqlite import ( "database/sql" _ "embed" "fmt" "justguestbook/internal/guestbook" _ "github.com/mattn/go-sqlite3" ) var ( //go:embed queries/schema.sql queryCreateDatabase string //go:embed queries/entryGetAll.sql queryGetEntries string //go:embed queries/entryCount.sql queryCount string //go:embed queries/entryNew.sql queryNewEntry string //go:embed queries/entryUpdate.sql queryUpdateEntry string //go:embed queries/entryDelete.sql queryDeleteEntry string //go:embed queries/replyNew.sql queryNewReply string //go:embed queries/replyUpdate.sql queryUpdateReply string //go:embed queries/replyDelete.sql queryDeleteReply string ) var ( stmtGetEntries *sql.Stmt stmtCount *sql.Stmt stmtNewEntry *sql.Stmt stmtUpdateEntry *sql.Stmt stmtDeleteEntry *sql.Stmt stmtNewReply *sql.Stmt stmtUpdateReply *sql.Stmt stmtDeleteReply *sql.Stmt ) func initDBStatements(db *sql.DB) error { _, err := db.Exec(queryCreateDatabase) if err != nil { return err } stmtGetEntries, err = db.Prepare(queryGetEntries) if err != nil { return err } stmtCount, err = db.Prepare(queryCount) if err != nil { return err } stmtNewEntry, err = db.Prepare(queryNewEntry) if err != nil { return err } stmtUpdateEntry, err = db.Prepare(queryUpdateEntry) if err != nil { return err } stmtDeleteEntry, err = db.Prepare(queryDeleteEntry) if err != nil { return err } stmtNewReply, err = db.Prepare(queryNewReply) if err != nil { return err } stmtUpdateReply, err = db.Prepare(queryUpdateReply) if err != nil { return err } stmtDeleteReply, err = db.Prepare(queryDeleteReply) if err != nil { return err } return nil } type SQLiteDatabase struct { db *sql.DB } func New(filePath string) (*SQLiteDatabase, error) { db, err := sql.Open("sqlite3", dsn(filePath)) if err != nil { return nil, err } if err := initDBStatements(db); err != nil { return nil, err } return &SQLiteDatabase{db: db}, nil } func (d *SQLiteDatabase) Entries(page, pageSize int64) (entries []*guestbook.Entry, err error) { tx, err := d.db.Begin() if err != nil { return } defer tx.Rollback() rows, err := tx.Stmt(stmtGetEntries).Query(pageSize, (page-1)*pageSize) if err != nil { return } defer rows.Close() for rows.Next() { var entry guestbook.Entry var reply_created sql.NullString var reply_message sql.NullString if err = rows.Scan( &entry.ID, &entry.Created, &entry.Name, &entry.Website, &entry.Message, &reply_created, &reply_message); err != nil { return } if reply_message.Valid /* reply_created is also valid if reply is */ { entry.Reply = &guestbook.Reply{ Created: reply_created.String, Message: reply_message.String} } entries = append(entries, &entry) } tx.Commit() return } func (d *SQLiteDatabase) Count() (int64, error) { tx, err := d.db.Begin() if err != nil { return -1, err } defer tx.Rollback() var count int64 err = tx.Stmt(stmtCount).QueryRow().Scan(&count) if err != nil { return -1, err } return count, nil } func (d *SQLiteDatabase) NewEntry(entry *guestbook.Entry) error { tx, err := d.db.Begin() if err != nil { return err } defer tx.Rollback() _, err = tx.Stmt(stmtNewEntry).Exec(entry.Created, entry.Name, entry.Message, entry.Website, entry.HideWebsite) if err != nil { return err } tx.Commit() return nil } func (d *SQLiteDatabase) UpdateEntry(entryID int64, entry *guestbook.Entry) (bool, error) { tx, err := d.db.Begin() if err != nil { return false, err } defer tx.Rollback() res, err := tx.Stmt(stmtUpdateEntry).Exec(entry.ID, entry.Name, entry.Message, entry.Website, entry.HideWebsite, entryID) if err != nil { return false, err } ra, err := res.RowsAffected() if err != nil { return false, err } return ra > 0, nil } func (d *SQLiteDatabase) DeleteEntry(entryID int64) error { tx, err := d.db.Begin() if err != nil { return err } defer tx.Rollback() res, err := tx.Stmt(stmtDeleteEntry).Exec(entryID) if err != nil { return err } _, err = res.RowsAffected() if err != nil { return err } return nil } func (d *SQLiteDatabase) NewReply(reply *guestbook.Reply) (err error) { tx, err := d.db.Begin() if err != nil { return } defer tx.Rollback() _, err = tx.Stmt(stmtNewReply).Exec(reply.ID, reply.Created, reply.Message) if err != nil { return } tx.Commit() return } // UpdateEntry func (d *SQLiteDatabase) UpdateReply(entryID int64, reply *guestbook.Reply) (bool, error) { tx, err := d.db.Begin() if err != nil { return false, err } defer tx.Rollback() res, err := tx.Stmt(stmtUpdateReply).Exec(reply.ID, reply.Created, reply.Message, entryID) if err != nil { return false, err } ra, err := res.RowsAffected() if err != nil { return false, err } return ra > 0, nil } func (d *SQLiteDatabase) DeleteReply(entryID int64) error { tx, err := d.db.Begin() if err != nil { return err } defer tx.Rollback() res, err := tx.Stmt(stmtDeleteReply).Exec(entryID) if err != nil { return err } _, err = res.RowsAffected() if err != nil { return err } return nil } func (d *SQLiteDatabase) Close() error { return d.db.Close() } func dsn(filePath string) string { return fmt.Sprintf("file:%s?_journal=WAL&_mutex=full", filePath) }