package dbmanager import ( "fmt" "os" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // SQLiteManager implements the DbManager interface for SQLite using GORM type SQLiteManager struct { db *gorm.DB dsn string } // NewSQLiteManager initializes a new SQLiteManager func NewSQLiteManager(dataSourceName string) (*SQLiteManager, error) { // First check if the database file exists isNewDb := !fileExists(dataSourceName) if isNewDb { // Create the database file if it doesn't exist file, err := os.Create(dataSourceName) if err != nil { return nil, fmt.Errorf("failed to create SQLite database file: %w", err) } defer file.Close() } // Open SQLite database with GORM and SQLite-specific configuration db, err := gorm.Open(sqlite.Open(dataSourceName), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), // Set DisableForeignKeyConstraintWhenMigrating to true to avoid foreign key issues during migration DisableForeignKeyConstraintWhenMigrating: true, }) if err != nil { return nil, fmt.Errorf("failed to connect to SQLite database: %w", err) } // Enable foreign keys in SQLite after migrations db.Exec("PRAGMA foreign_keys = ON") return &SQLiteManager{ db: db, dsn: dataSourceName, }, nil } // fileExists checks if a file exists func fileExists(filename string) bool { _, err := os.Stat(filename) return !os.IsNotExist(err) } // GetDB returns the GORM database instance func (m *SQLiteManager) GetDB() *gorm.DB { return m.db } // Connect establishes a connection to the SQLite database func (m *SQLiteManager) Connect() error { // Connection is already established in NewSQLiteManager return nil } // Disconnect closes the connection to the SQLite database func (m *SQLiteManager) Disconnect() error { sqlDB, err := m.db.DB() if err != nil { return err } return sqlDB.Close() } // Migrate runs auto migration for the provided models func (m *SQLiteManager) Migrate(models ...interface{}) error { // Check if database file exists before isNewDb := !fileExists(m.dsn) // SQLite has limitations with ALTER TABLE for adding NOT NULL columns // For a new database, we can just create tables directly if isNewDb { return m.db.AutoMigrate(models...) } // For existing database, we need a more careful approach to avoid NOT NULL errors // Try to create tables that don't exist migrator := m.db.Migrator() for _, model := range models { // Check if the table exists if !migrator.HasTable(model) { // If table doesn't exist, create it if err := migrator.CreateTable(model); err != nil { return fmt.Errorf("failed to create table for %T: %w", model, err) } } } // Re-enable foreign key constraints m.db.Exec("PRAGMA foreign_keys = ON") return nil }