/server/db/db.go

https://github.com/sentriz/gonic · Go · 155 lines · 132 code · 15 blank · 8 comment · 14 complexity · abbf4ddf8ced688f9a91d7adcd3debcf MD5 · raw file

  1. package db
  2. import (
  3. "fmt"
  4. "log"
  5. "net/url"
  6. "os"
  7. "github.com/gorilla/securecookie"
  8. "github.com/jinzhu/gorm"
  9. "gopkg.in/gormigrate.v1"
  10. )
  11. // wrapMigrations wraps a list of migrations to add logging and transactions
  12. func wrapMigrations(migrs ...gormigrate.Migration) []*gormigrate.Migration {
  13. log := func(i int, mig gormigrate.MigrateFunc, name string) gormigrate.MigrateFunc {
  14. return func(db *gorm.DB) error {
  15. // print that we're on the ith out of n migrations
  16. defer log.Printf("migration (%d/%d) '%s' finished", i+1, len(migrs), name)
  17. return db.Transaction(mig)
  18. }
  19. }
  20. ret := make([]*gormigrate.Migration, 0, len(migrs))
  21. for i, mig := range migrs {
  22. ret = append(ret, &gormigrate.Migration{
  23. ID: mig.ID,
  24. Rollback: mig.Rollback,
  25. Migrate: log(i, mig.Migrate, mig.ID),
  26. })
  27. }
  28. return ret
  29. }
  30. func defaultOptions() url.Values {
  31. return url.Values{
  32. // with this, multiple connections share a single data and schema cache.
  33. // see https://www.sqlite.org/sharedcache.html
  34. "cache": {"shared"},
  35. // with this, the db sleeps for a little while when locked. can prevent
  36. // a SQLITE_BUSY. see https://www.sqlite.org/c3ref/busy_timeout.html
  37. "_busy_timeout": {"30000"},
  38. "_journal_mode": {"WAL"},
  39. "_foreign_keys": {"true"},
  40. }
  41. }
  42. type DB struct {
  43. *gorm.DB
  44. }
  45. func New(path string) (*DB, error) {
  46. // https://github.com/mattn/go-sqlite3#connection-string
  47. url := url.URL{
  48. Scheme: "file",
  49. Opaque: path,
  50. }
  51. url.RawQuery = defaultOptions().Encode()
  52. db, err := gorm.Open("sqlite3", url.String())
  53. if err != nil {
  54. return nil, fmt.Errorf("with gorm: %w", err)
  55. }
  56. db.SetLogger(log.New(os.Stdout, "gorm ", 0))
  57. db.DB().SetMaxOpenConns(1)
  58. migrOptions := &gormigrate.Options{
  59. TableName: "migrations",
  60. IDColumnName: "id",
  61. IDColumnSize: 255,
  62. UseTransaction: false,
  63. }
  64. migr := gormigrate.New(db, migrOptions, wrapMigrations(
  65. migrateInitSchema(),
  66. migrateCreateInitUser(),
  67. migrateMergePlaylist(),
  68. migrateCreateTranscode(),
  69. migrateAddGenre(),
  70. migrateUpdateTranscodePrefIDX(),
  71. migrateAddAlbumIDX(),
  72. ))
  73. if err = migr.Migrate(); err != nil {
  74. return nil, fmt.Errorf("migrating to latest version: %w", err)
  75. }
  76. return &DB{DB: db}, nil
  77. }
  78. func NewMock() (*DB, error) {
  79. return New(":memory:")
  80. }
  81. func (db *DB) GetSetting(key string) string {
  82. setting := &Setting{}
  83. db.
  84. Where("key=?", key).
  85. First(setting)
  86. return setting.Value
  87. }
  88. func (db *DB) SetSetting(key, value string) {
  89. db.
  90. Where(Setting{Key: key}).
  91. Assign(Setting{Value: value}).
  92. FirstOrCreate(&Setting{})
  93. }
  94. func (db *DB) GetOrCreateKey(key string) string {
  95. value := db.GetSetting(key)
  96. if value == "" {
  97. value = string(securecookie.GenerateRandomKey(32))
  98. db.SetSetting(key, value)
  99. }
  100. return value
  101. }
  102. func (db *DB) GetUserByID(id int) *User {
  103. user := &User{}
  104. err := db.
  105. Where("id=?", id).
  106. First(user).
  107. Error
  108. if gorm.IsRecordNotFoundError(err) {
  109. return nil
  110. }
  111. return user
  112. }
  113. func (db *DB) GetUserByName(name string) *User {
  114. user := &User{}
  115. err := db.
  116. Where("name=?", name).
  117. First(user).
  118. Error
  119. if gorm.IsRecordNotFoundError(err) {
  120. return nil
  121. }
  122. return user
  123. }
  124. type ChunkFunc func(*gorm.DB, []int64) error
  125. func (db *DB) TransactionChunked(data []int64, cb ChunkFunc) error {
  126. // https://sqlite.org/limits.html
  127. const size = 999
  128. return db.Transaction(func(tx *gorm.DB) error {
  129. for i := 0; i < len(data); i += size {
  130. end := i + size
  131. if end > len(data) {
  132. end = len(data)
  133. }
  134. if err := cb(tx, data[i:end]); err != nil {
  135. return err
  136. }
  137. }
  138. return nil
  139. })
  140. }