// Package database provides SQLite database access. package database import ( "context" "database/sql" "embed" "fmt" "io/fs" "log/slog" "path" "path/filepath" _ "modernc.org/sqlite" // Register sqlite driver. "yellowjacket/backend/database/sql/sqlcgen" "yellowjacket/backend/system" ) //go:generate go tool sqlc generate //go:embed sql/schemas/*.sql var schemas embed.FS // DB wraps the SQLite database connection and queries. type DB struct { db *sql.DB Ctx context.Context Queries *sqlcgen.Queries logger *slog.Logger } // NewDB opens the database and applies schema migrations. func NewDB(logger *slog.Logger) (*DB, error) { dbCtx := context.Background() userDataDir, err := system.GetUserDataDirPath() if err != nil { return nil, fmt.Errorf("could not get user data directory: %w", err) } sqliteDBFilePath := path.Join(userDataDir, "yj.db") logger.Debug("opening sqlite database", "filepath", sqliteDBFilePath) db, err := sql.Open("sqlite", sqliteDBFilePath+"?_busy_timeout=5000&_journal_mode=WAL") if err != nil { return nil, fmt.Errorf("could not connect to sqlite database: %w", err) } db.SetMaxOpenConns(1) // SQLite only supports one writer at a time // Execute SQL files from the embedded schemas directory logger.Debug("reading sql schema files from embedded directory") dirEntries, err := schemas.ReadDir("sql/schemas") if err != nil { return nil, fmt.Errorf("could not read schemas directory: %w", err) } logger.Debug("executing all sql schema files") for _, dirEntry := range dirEntries { if !dirEntry.IsDir() { filePath := filepath.Join("sql/schemas", dirEntry.Name()) sqlContent, err := fs.ReadFile(schemas, filePath) if err != nil { return nil, fmt.Errorf("could not read file %s: %w", filePath, err) } logger.Debug( "executing sql schema file", "filepath", filePath, "sql", string(sqlContent), ) _, err = db.ExecContext(dbCtx, string(sqlContent)) // Execute the SQL if err != nil { return nil, fmt.Errorf("error executing sql from file %s: %w", filePath, err) } } } // Get generated queries queries := sqlcgen.New(db) return &DB{ db: db, Ctx: dbCtx, Queries: queries, logger: logger, }, err }