2020-01-02 00:03:07 +01:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
2020-01-06 02:14:17 +01:00
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
2020-01-02 00:03:07 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
2020-01-08 19:51:21 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/mutex"
|
2020-01-02 00:03:07 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/tidb"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DatabaseDriver returns the database driver name.
|
|
|
|
func (c *Config) DatabaseDriver() string {
|
2020-04-28 22:49:02 +02:00
|
|
|
if strings.ToLower(c.params.DatabaseDriver) == "mysql" {
|
|
|
|
return DriverMysql
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-04-28 22:49:02 +02:00
|
|
|
return DriverTidb
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// DatabaseDsn returns the database data source name (DSN).
|
|
|
|
func (c *Config) DatabaseDsn() string {
|
2020-04-13 18:08:21 +02:00
|
|
|
if c.params.DatabaseDsn == "" {
|
2020-04-28 21:10:31 +02:00
|
|
|
return "root:photoprism@tcp(localhost:2343)/photoprism?parseTime=true"
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-04-13 18:08:21 +02:00
|
|
|
return c.params.DatabaseDsn
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Db returns the db connection.
|
|
|
|
func (c *Config) Db() *gorm.DB {
|
|
|
|
if c.db == nil {
|
2020-04-30 20:07:03 +02:00
|
|
|
log.Fatal("config: database not connected")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return c.db
|
|
|
|
}
|
|
|
|
|
|
|
|
// CloseDb closes the db connection (if any).
|
|
|
|
func (c *Config) CloseDb() error {
|
|
|
|
if c.db != nil {
|
|
|
|
if err := c.db.Close(); err == nil {
|
|
|
|
c.db = nil
|
|
|
|
} else {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-30 20:07:03 +02:00
|
|
|
// InitDb will initialize the database connection and schema.
|
|
|
|
func (c *Config) InitDb() {
|
|
|
|
entity.SetDbProvider(c)
|
2020-04-30 21:21:09 +02:00
|
|
|
entity.MigrateDb()
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
2020-04-30 21:21:09 +02:00
|
|
|
// ResetDb drops all tables in the currently configured database and re-creates them.
|
2020-05-08 15:41:01 +02:00
|
|
|
func (c *Config) ResetDb() {
|
2020-04-30 21:21:09 +02:00
|
|
|
entity.SetDbProvider(c)
|
2020-05-08 15:41:01 +02:00
|
|
|
entity.InitTestFixtures()
|
2020-04-08 13:24:06 +02:00
|
|
|
}
|
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
// connectToDatabase establishes a database connection.
|
|
|
|
// When used with the internal driver, it may create a new database server instance.
|
|
|
|
// It tries to do this 12 times with a 5 second sleep interval in between.
|
|
|
|
func (c *Config) connectToDatabase(ctx context.Context) error {
|
2020-01-08 19:51:21 +01:00
|
|
|
mutex.Db.Lock()
|
|
|
|
defer mutex.Db.Unlock()
|
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
dbDriver := c.DatabaseDriver()
|
|
|
|
dbDsn := c.DatabaseDsn()
|
|
|
|
|
|
|
|
if dbDriver == "" {
|
2020-01-06 02:14:17 +01:00
|
|
|
return errors.New("config: database driver not specified")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if dbDsn == "" {
|
2020-01-06 02:14:17 +01:00
|
|
|
return errors.New("config: database DSN not specified")
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
isTiDB := false
|
|
|
|
initSuccess := false
|
|
|
|
|
2020-04-28 22:49:02 +02:00
|
|
|
if dbDriver == DriverTidb {
|
2020-01-02 00:03:07 +01:00
|
|
|
isTiDB = true
|
2020-04-28 22:49:02 +02:00
|
|
|
dbDriver = DriverMysql
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
db, err := gorm.Open(dbDriver, dbDsn)
|
|
|
|
if err != nil || db == nil {
|
|
|
|
if isTiDB {
|
2020-04-28 22:49:02 +02:00
|
|
|
log.Infof("starting database server at %s:%d\n", c.TidbServerHost(), c.TidbServerPort())
|
2020-01-02 00:03:07 +01:00
|
|
|
|
2020-04-28 22:49:02 +02:00
|
|
|
go tidb.Start(ctx, c.TidbServerPath(), c.TidbServerPort(), c.TidbServerHost(), c.Debug())
|
2020-04-30 20:07:03 +02:00
|
|
|
|
2020-05-01 17:31:42 +02:00
|
|
|
time.Sleep(5 * time.Second)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := 1; i <= 12; i++ {
|
|
|
|
db, err = gorm.Open(dbDriver, dbDsn)
|
|
|
|
|
|
|
|
if db != nil && err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if isTiDB && !initSuccess {
|
2020-04-28 22:49:02 +02:00
|
|
|
err = tidb.InitDatabase(c.TidbServerPort(), c.TidbServerPassword())
|
2020-01-02 00:03:07 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Debug(err)
|
|
|
|
} else {
|
|
|
|
initSuccess = true
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 20:07:03 +02:00
|
|
|
|
|
|
|
time.Sleep(5 * time.Second)
|
2020-01-02 00:03:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil || db == nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-06 14:32:15 +01:00
|
|
|
db.LogMode(false)
|
|
|
|
db.SetLogger(log)
|
|
|
|
|
2020-01-02 00:03:07 +01:00
|
|
|
c.db = db
|
|
|
|
return err
|
|
|
|
}
|
2020-01-06 02:14:17 +01:00
|
|
|
|
|
|
|
// ImportSQL imports a file to the currently configured database.
|
|
|
|
func (c *Config) ImportSQL(filename string) {
|
|
|
|
contents, err := ioutil.ReadFile(filename)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
statements := strings.Split(string(contents), ";\n")
|
2020-01-06 04:24:49 +01:00
|
|
|
q := c.Db().Unscoped()
|
2020-01-06 02:14:17 +01:00
|
|
|
|
|
|
|
for _, stmt := range statements {
|
|
|
|
// Skip empty lines and comments
|
|
|
|
if len(stmt) < 3 || stmt[0] == '#' || stmt[0] == ';' {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-01-06 14:32:15 +01:00
|
|
|
var result struct{}
|
2020-01-06 04:24:49 +01:00
|
|
|
|
2020-04-30 15:41:47 +02:00
|
|
|
q.Raw(stmt).Scan(&result)
|
2020-01-06 02:14:17 +01:00
|
|
|
}
|
|
|
|
}
|