package peachy import ( "errors" "fmt" "os" "zombiezen.com/go/sqlite" "zombiezen.com/go/sqlite/sqlitex" ) const AppID = '🍑' var ErrInvalidDB = errors.New("invalid database file") var ErrFileExists = errors.New("database file already exists") var ErrFileNotExist = errors.New("database file does not exist") type DBError struct{ error } func (dbe DBError) Error() string { return "database error: " + dbe.error.Error() } type DB struct { conn *sqlite.Conn } func (db *DB) Close() { db.conn.Close() } func setupConn(conn *sqlite.Conn) error { return nil } func Open(path string) (db *DB, err error) { var conn *sqlite.Conn defer func() { if err != nil { conn.Close() } }() conn, err = sqlite.OpenConn(path, sqlite.OpenReadWrite|sqlite.OpenWAL) switch sqlite.ErrCode(err) { case sqlite.ResultOK: case sqlite.ResultCantOpen: return nil, ErrFileNotExist case sqlite.ResultNotADB: return nil, ErrInvalidDB default: return nil, DBError{err} } var goodAppID bool sqlitex.ExecuteTransient(conn, "PRAGMA application_id", &sqlitex.ExecOptions{ ResultFunc: func(stmt *sqlite.Stmt) error { goodAppID = stmt.ColumnInt32(0) == AppID return nil }}) if !goodAppID { return nil, ErrInvalidDB } err = setupConn(conn) if err != nil { return nil, err } return &DB{conn: conn}, nil } func Create(path string) (db *DB, err error) { var conn *sqlite.Conn defer func() { if err != nil { conn.Close() } }() finfo, _ := os.Stat(path) if finfo != nil { return nil, ErrFileExists } conn, err = sqlite.OpenConn(path, sqlite.OpenCreate|sqlite.OpenReadWrite|sqlite.OpenWAL) if err != nil { return nil, fmt.Errorf("could not create database: %w", err) } query := fmt.Sprintf("PRAGMA application_id=%d", AppID) err = sqlitex.ExecuteTransient(conn, query, nil) if err != nil { return nil, DBError{err} } err = setupConn(conn) if err != nil { return nil, err } return &DB{conn: conn}, nil }