/internal/config/db.go

https://github.com/photoprism/photoprism · Go · 194 lines · 141 code · 42 blank · 11 comment · 43 complexity · 9b4e6357db79f4141b2e3754f8f341b1 MD5 · raw file

  1. package config
  2. import (
  3. "errors"
  4. "io/ioutil"
  5. "path/filepath"
  6. "runtime"
  7. "strings"
  8. "time"
  9. "github.com/jinzhu/gorm"
  10. _ "github.com/jinzhu/gorm/dialects/mysql"
  11. _ "github.com/jinzhu/gorm/dialects/sqlite"
  12. "github.com/photoprism/photoprism/internal/entity"
  13. "github.com/photoprism/photoprism/internal/mutex"
  14. )
  15. // DatabaseDriver returns the database driver name.
  16. func (c *Config) DatabaseDriver() string {
  17. switch strings.ToLower(c.params.DatabaseDriver) {
  18. case MySQL, "mariadb":
  19. c.params.DatabaseDriver = MySQL
  20. case SQLite, "sqlite", "sqllite", "test", "file", "":
  21. c.params.DatabaseDriver = SQLite
  22. case "tidb":
  23. log.Warnf("config: database driver 'tidb' is deprecated, using sqlite")
  24. c.params.DatabaseDriver = SQLite
  25. c.params.DatabaseDsn = ""
  26. default:
  27. log.Warnf("config: unsupported database driver %s, using sqlite", c.params.DatabaseDriver)
  28. c.params.DatabaseDriver = SQLite
  29. c.params.DatabaseDsn = ""
  30. }
  31. return c.params.DatabaseDriver
  32. }
  33. // DatabaseDsn returns the database data source name (DSN).
  34. func (c *Config) DatabaseDsn() string {
  35. if c.params.DatabaseDsn == "" {
  36. switch c.DatabaseDriver() {
  37. case MySQL:
  38. return "photoprism:photoprism@tcp(photoprism-db:3306)/photoprism?parseTime=true"
  39. case SQLite:
  40. return filepath.Join(c.StoragePath(), "index.db")
  41. default:
  42. log.Errorf("config: empty database dsn")
  43. return ""
  44. }
  45. }
  46. return c.params.DatabaseDsn
  47. }
  48. // DatabaseConns returns the maximum number of open connections to the database.
  49. func (c *Config) DatabaseConns() int {
  50. limit := c.params.DatabaseConns
  51. if limit <= 0 {
  52. limit = (runtime.NumCPU() * 2) + 16
  53. }
  54. if limit > 1024 {
  55. limit = 1024
  56. }
  57. return limit
  58. }
  59. // DatabaseConnsIdle returns the maximum number of idle connections to the database (equal or less than open).
  60. func (c *Config) DatabaseConnsIdle() int {
  61. limit := c.params.DatabaseConnsIdle
  62. if limit <= 0 {
  63. limit = runtime.NumCPU() + 8
  64. }
  65. if limit > c.DatabaseConns() {
  66. limit = c.DatabaseConns()
  67. }
  68. return limit
  69. }
  70. // Db returns the db connection.
  71. func (c *Config) Db() *gorm.DB {
  72. if c.db == nil {
  73. log.Fatal("config: database not connected")
  74. }
  75. return c.db
  76. }
  77. // CloseDb closes the db connection (if any).
  78. func (c *Config) CloseDb() error {
  79. if c.db != nil {
  80. if err := c.db.Close(); err == nil {
  81. c.db = nil
  82. } else {
  83. return err
  84. }
  85. }
  86. return nil
  87. }
  88. // InitDb will initialize the database connection and schema.
  89. func (c *Config) InitDb() {
  90. entity.SetDbProvider(c)
  91. entity.MigrateDb()
  92. entity.Admin.InitPassword(c.AdminPassword())
  93. go entity.SaveErrorMessages()
  94. }
  95. // InitTestDb drops all tables in the currently configured database and re-creates them.
  96. func (c *Config) InitTestDb() {
  97. entity.SetDbProvider(c)
  98. entity.ResetTestFixtures()
  99. entity.Admin.InitPassword(c.AdminPassword())
  100. go entity.SaveErrorMessages()
  101. }
  102. // connectDb establishes a database connection.
  103. func (c *Config) connectDb() error {
  104. mutex.Db.Lock()
  105. defer mutex.Db.Unlock()
  106. dbDriver := c.DatabaseDriver()
  107. dbDsn := c.DatabaseDsn()
  108. if dbDriver == "" {
  109. return errors.New("config: database driver not specified")
  110. }
  111. if dbDsn == "" {
  112. return errors.New("config: database DSN not specified")
  113. }
  114. db, err := gorm.Open(dbDriver, dbDsn)
  115. if err != nil || db == nil {
  116. for i := 1; i <= 12; i++ {
  117. db, err = gorm.Open(dbDriver, dbDsn)
  118. if db != nil && err == nil {
  119. break
  120. }
  121. time.Sleep(5 * time.Second)
  122. }
  123. if err != nil || db == nil {
  124. log.Fatal(err)
  125. }
  126. }
  127. db.LogMode(false)
  128. db.SetLogger(log)
  129. db.DB().SetMaxOpenConns(c.DatabaseConns())
  130. db.DB().SetMaxIdleConns(c.DatabaseConnsIdle())
  131. db.DB().SetConnMaxLifetime(10 * time.Minute)
  132. c.db = db
  133. return err
  134. }
  135. // ImportSQL imports a file to the currently configured database.
  136. func (c *Config) ImportSQL(filename string) {
  137. contents, err := ioutil.ReadFile(filename)
  138. if err != nil {
  139. log.Error(err)
  140. return
  141. }
  142. statements := strings.Split(string(contents), ";\n")
  143. q := c.Db().Unscoped()
  144. for _, stmt := range statements {
  145. // Skip empty lines and comments
  146. if len(stmt) < 3 || stmt[0] == '#' || stmt[0] == ';' {
  147. continue
  148. }
  149. var result struct{}
  150. q.Raw(stmt).Scan(&result)
  151. }
  152. }