package database import ( domainUpdate "RenewCMS/internal/domain/update" "errors" "fmt" "os" "path/filepath" "time" "github.com/glebarez/sqlite" "gorm.io/gorm" ) const DriverSQLite = "sqlite" const ( sqliteBusyTimeoutMs = 5000 sqliteResultBusy = 5 sqliteResultLocked = 6 sqlitePrimaryMask = 0xff sqliteUniqueKey = 2067 sqlitePrimaryKey = 1555 ) type sqliteDriver struct{} func SQLiteDSN(dbName string) string { return fmt.Sprintf("%s?_pragma=busy_timeout(%d)", dbName, sqliteBusyTimeoutMs) } func (sqliteDriver) Open(file string) (*gorm.DB, error) { if err := os.MkdirAll(filepath.Dir(file), os.ModePerm); err != nil { return nil, fmt.Errorf("unable to create necessary subdirectories: %w", err) } return gorm.Open(sqlite.Open(SQLiteDSN(file)), &gorm.Config{}) } func (sqliteDriver) NewBackup(db *gorm.DB, directory string, now func() time.Time) domainUpdate.DatabaseBackup { return NewSQLiteBackup(db, directory, now) } func (sqliteDriver) IsLockContention(err error) bool { code, ok := sqliteCode(err) primary := code & sqlitePrimaryMask return ok && (primary == sqliteResultBusy || primary == sqliteResultLocked) } func (sqliteDriver) IsUniqueViolation(err error) bool { code, ok := sqliteCode(err) return ok && (code == sqliteUniqueKey || code == sqlitePrimaryKey) } func sqliteCode(err error) (int, bool) { var coded interface{ Code() int } if !errors.As(err, &coded) { return 0, false } return coded.Code(), true }