1package db
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "log/slog"
8 "path/filepath"
9
10 _ "github.com/ncruces/go-sqlite3/driver"
11 _ "github.com/ncruces/go-sqlite3/embed"
12
13 "github.com/pressly/goose/v3"
14)
15
16func Connect(ctx context.Context, dataDir string) (*sql.DB, error) {
17 if dataDir == "" {
18 return nil, fmt.Errorf("data.dir is not set")
19 }
20 dbPath := filepath.Join(dataDir, "crush.db")
21 // Open the SQLite database
22 db, err := sql.Open("sqlite3", dbPath)
23 if err != nil {
24 return nil, fmt.Errorf("failed to open database: %w", err)
25 }
26
27 // Verify connection
28 if err = db.PingContext(ctx); err != nil {
29 db.Close()
30 return nil, fmt.Errorf("failed to connect to database: %w", err)
31 }
32
33 // Set pragmas for better performance
34 pragmas := []string{
35 "PRAGMA foreign_keys = ON;",
36 "PRAGMA journal_mode = WAL;",
37 "PRAGMA page_size = 4096;",
38 "PRAGMA cache_size = -8000;",
39 "PRAGMA synchronous = NORMAL;",
40 }
41
42 for _, pragma := range pragmas {
43 if _, err = db.ExecContext(ctx, pragma); err != nil {
44 slog.Error("Failed to set pragma", pragma, err)
45 } else {
46 slog.Debug("Set pragma", "pragma", pragma)
47 }
48 }
49
50 goose.SetBaseFS(FS)
51
52 if err := goose.SetDialect("sqlite3"); err != nil {
53 slog.Error("Failed to set dialect", "error", err)
54 return nil, fmt.Errorf("failed to set dialect: %w", err)
55 }
56
57 if err := goose.Up(db, "migrations"); err != nil {
58 slog.Error("Failed to apply migrations", "error", err)
59 return nil, fmt.Errorf("failed to apply migrations: %w", err)
60 }
61 return db, nil
62}