1package db
2
3import (
4 "database/sql"
5 "fmt"
6 "os"
7 "path/filepath"
8
9 "github.com/golang-migrate/migrate/v4"
10 "github.com/golang-migrate/migrate/v4/source/iofs"
11
12 "github.com/golang-migrate/migrate/v4/database/sqlite3"
13 _ "github.com/mattn/go-sqlite3"
14
15 "github.com/kujtimiihoxha/termai/internal/config"
16 "github.com/kujtimiihoxha/termai/internal/logging"
17)
18
19var log = logging.Get()
20
21func Connect() (*sql.DB, error) {
22 dataDir := config.Get().Data.Directory
23 if dataDir == "" {
24 return nil, fmt.Errorf("data.dir is not set")
25 }
26 if err := os.MkdirAll(dataDir, 0o700); err != nil {
27 return nil, fmt.Errorf("failed to create data directory: %w", err)
28 }
29 dbPath := filepath.Join(dataDir, "termai.db")
30 // Open the SQLite database
31 db, err := sql.Open("sqlite3", dbPath)
32 if err != nil {
33 return nil, fmt.Errorf("failed to open database: %w", err)
34 }
35
36 // Verify connection
37 if err = db.Ping(); err != nil {
38 db.Close()
39 return nil, fmt.Errorf("failed to connect to database: %w", err)
40 }
41
42 // Set pragmas for better performance
43 pragmas := []string{
44 "PRAGMA foreign_keys = ON;",
45 "PRAGMA journal_mode = WAL;",
46 "PRAGMA page_size = 4096;",
47 "PRAGMA cache_size = -8000;",
48 "PRAGMA synchronous = NORMAL;",
49 }
50
51 for _, pragma := range pragmas {
52 if _, err = db.Exec(pragma); err != nil {
53 log.Warn("Failed to set pragma", pragma, err)
54 } else {
55 log.Warn("Set pragma", "pragma", pragma)
56 }
57 }
58
59 // Initialize schema from embedded file
60 d, err := iofs.New(FS, "migrations")
61 if err != nil {
62 log.Error("Failed to open embedded migrations", "error", err)
63 db.Close()
64 return nil, fmt.Errorf("failed to open embedded migrations: %w", err)
65 }
66
67 driver, err := sqlite3.WithInstance(db, &sqlite3.Config{})
68 if err != nil {
69 log.Error("Failed to create SQLite driver", "error", err)
70 db.Close()
71 return nil, fmt.Errorf("failed to create SQLite driver: %w", err)
72 }
73
74 m, err := migrate.NewWithInstance("iofs", d, "ql", driver)
75 if err != nil {
76 log.Error("Failed to create migration instance", "error", err)
77 db.Close()
78 return nil, fmt.Errorf("failed to create migration instance: %w", err)
79 }
80
81 err = m.Up()
82 if err != nil && err != migrate.ErrNoChange {
83 log.Error("Migration failed", "error", err)
84 db.Close()
85 return nil, fmt.Errorf("failed to apply schema: %w", err)
86 } else if err == migrate.ErrNoChange {
87 log.Info("No schema changes to apply")
88 } else {
89 log.Info("Schema migration applied successfully")
90 }
91
92 return db, nil
93}