connect.go 2.4 KB

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