package sqlite import ( "context" "database/sql" "fmt" "time" "modernc.org/sqlite" "git.hrafn.xyz/aether/notes/internal/models" ) const sqlSet = ` PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 7000; ` type SQLiteStore struct { read *sql.DB write *sql.DB } func NewSQLiteStore(ctx context.Context, dbPath string) (*SQLiteStore, error) { sqliteDB := &SQLiteStore{} sqlite.RegisterConnectionHook(func(conn sqlite.ExecQuerierContext, _ string) error { _, err := conn.ExecContext(ctx, sqlSet, nil) return err }) write, err := sql.Open("sqlite", "file:"+dbPath) if err != nil { return nil, err } err = write.PingContext(ctx) if err != nil { return nil, fmt.Errorf("failed to ping database: %w", err) } // only a single writer ever, no concurrency I don't trust it write.SetMaxOpenConns(1) write.SetConnMaxIdleTime(time.Minute) read, err := sql.Open("sqlite", "file:"+dbPath) if err != nil { return nil, err } err = read.PingContext(ctx) if err != nil { return nil, fmt.Errorf("failed to ping database: %w", err) } // readers can be concurrent read.SetMaxOpenConns(100) read.SetConnMaxIdleTime(time.Minute) sqliteDB.read = read sqliteDB.write = write err = sqliteDB.validateSchema(ctx) if err != nil { return nil, fmt.Errorf("failed to validate schema: %w", err) } return sqliteDB, nil } func (s *SQLiteStore) Close() error { readErr := s.read.Close() writeErr := s.write.Close() if readErr != nil || writeErr != nil { return fmt.Errorf("failed to close connections: read error: %v, write error: %v", readErr, writeErr) } return nil } func (s *SQLiteStore) SaveNote(ctx context.Context, note models.Note) (models.Note, error) { return models.Note{}, fmt.Errorf("not implemented") } func (s *SQLiteStore) validateSchema(ctx context.Context) error { _, err := s.write.ExecContext(ctx, ` CREATE TABLE IF NOT EXISTS notes ( id INTEGER PRIMARY KEY AUTOINCREMENT, last_update TIMESTAMP NOT NULL, content TEXT NOT NULL ); `, nil) return err }