2018-11-06 19:02:03 +01:00
|
|
|
/*
|
2020-02-21 01:14:45 +01:00
|
|
|
Package entity contains models for data storage based on GORM.
|
2018-11-06 19:02:03 +01:00
|
|
|
|
|
|
|
See http://gorm.io/docs/ for more information about GORM.
|
|
|
|
|
|
|
|
Additional information concerning data storage can be found in our Developer Guide:
|
|
|
|
|
|
|
|
https://github.com/photoprism/photoprism/wiki/Storage
|
|
|
|
*/
|
2019-12-11 16:55:18 +01:00
|
|
|
package entity
|
2019-12-11 19:11:44 +01:00
|
|
|
|
|
|
|
import (
|
2020-05-08 17:45:32 +02:00
|
|
|
"fmt"
|
2020-04-30 21:21:09 +02:00
|
|
|
"time"
|
|
|
|
|
2019-12-11 19:11:44 +01:00
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
"github.com/photoprism/photoprism/internal/event"
|
|
|
|
)
|
|
|
|
|
|
|
|
var log = event.Log
|
2020-12-05 00:13:44 +01:00
|
|
|
var GeoApi = "places"
|
2019-12-11 19:11:44 +01:00
|
|
|
|
|
|
|
func logError(result *gorm.DB) {
|
|
|
|
if result.Error != nil {
|
|
|
|
log.Error(result.Error.Error())
|
|
|
|
}
|
|
|
|
}
|
2020-04-30 14:32:48 +02:00
|
|
|
|
2020-05-08 17:45:32 +02:00
|
|
|
type Types map[string]interface{}
|
|
|
|
|
2021-08-11 21:42:31 +02:00
|
|
|
// Entities contains database entities and their table names.
|
2020-05-08 17:45:32 +02:00
|
|
|
var Entities = Types{
|
2021-08-16 00:29:36 +02:00
|
|
|
"errors": &Error{},
|
|
|
|
"addresses": &Address{},
|
|
|
|
"users": &User{},
|
|
|
|
"accounts": &Account{},
|
|
|
|
"folders": &Folder{},
|
|
|
|
"duplicates": &Duplicate{},
|
|
|
|
"files": &File{},
|
|
|
|
"files_share": &FileShare{},
|
|
|
|
"files_sync": &FileSync{},
|
|
|
|
"photos": &Photo{},
|
|
|
|
"details": &Details{},
|
|
|
|
"places": &Place{},
|
|
|
|
"cells": &Cell{},
|
|
|
|
"cameras": &Camera{},
|
|
|
|
"lenses": &Lens{},
|
|
|
|
"countries": &Country{},
|
|
|
|
"albums": &Album{},
|
|
|
|
"photos_albums": &PhotoAlbum{},
|
|
|
|
"labels": &Label{},
|
|
|
|
"categories": &Category{},
|
|
|
|
"photos_labels": &PhotoLabel{},
|
|
|
|
"keywords": &Keyword{},
|
|
|
|
"photos_keywords": &PhotoKeyword{},
|
|
|
|
"passwords": &Password{},
|
|
|
|
"links": &Link{},
|
|
|
|
Subject{}.TableName(): &Subject{},
|
|
|
|
Face{}.TableName(): &Face{},
|
|
|
|
Marker{}.TableName(): &Marker{},
|
2020-05-08 17:45:32 +02:00
|
|
|
}
|
|
|
|
|
2020-05-30 14:52:47 +02:00
|
|
|
type RowCount struct {
|
|
|
|
Count int
|
|
|
|
}
|
|
|
|
|
2020-05-08 17:45:32 +02:00
|
|
|
// WaitForMigration waits for the database migration to be successful.
|
|
|
|
func (list Types) WaitForMigration() {
|
|
|
|
attempts := 100
|
|
|
|
for name := range list {
|
|
|
|
for i := 0; i <= attempts; i++ {
|
2020-05-30 14:52:47 +02:00
|
|
|
count := RowCount{}
|
|
|
|
if err := Db().Raw(fmt.Sprintf("SELECT COUNT(*) AS count FROM %s", name)).Scan(&count).Error; err == nil {
|
2020-05-09 11:00:22 +02:00
|
|
|
// log.Debugf("entity: table %s migrated", name)
|
2020-05-08 17:45:32 +02:00
|
|
|
break
|
|
|
|
} else {
|
2020-05-09 11:00:22 +02:00
|
|
|
log.Debugf("entity: wait for migration %s (%s)", err.Error(), name)
|
2020-05-08 17:45:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if i == attempts {
|
|
|
|
panic("migration failed")
|
|
|
|
}
|
|
|
|
|
|
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
// Truncate removes all data from tables without dropping them.
|
|
|
|
func (list Types) Truncate() {
|
|
|
|
for name := range list {
|
2020-07-20 19:48:31 +02:00
|
|
|
if err := Db().Exec(fmt.Sprintf("DELETE FROM %s WHERE 1", name)).Error; err == nil {
|
2020-10-03 13:50:30 +02:00
|
|
|
// log.Debugf("entity: removed all data from %s", name)
|
2020-05-08 19:34:29 +02:00
|
|
|
break
|
2020-05-09 11:00:22 +02:00
|
|
|
} else if err.Error() != "record not found" {
|
2020-07-20 19:48:31 +02:00
|
|
|
log.Debugf("entity: %s in %s", err, name)
|
2020-05-08 19:34:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-11 21:42:31 +02:00
|
|
|
// Migrate migrates all database tables of registered entities.
|
2020-05-08 17:45:32 +02:00
|
|
|
func (list Types) Migrate() {
|
|
|
|
for _, entity := range list {
|
2020-05-08 18:18:19 +02:00
|
|
|
if err := UnscopedDb().AutoMigrate(entity).Error; err != nil {
|
2020-05-09 11:00:22 +02:00
|
|
|
log.Debugf("entity: migrate %s (waiting 1s)", err.Error())
|
2020-05-08 18:18:19 +02:00
|
|
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
|
|
|
if err := UnscopedDb().AutoMigrate(entity).Error; err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-05-08 17:45:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop drops all database tables of registered entities.
|
|
|
|
func (list Types) Drop() {
|
|
|
|
for _, entity := range list {
|
2020-05-08 18:18:19 +02:00
|
|
|
if err := UnscopedDb().DropTableIfExists(entity).Error; err != nil {
|
2020-05-08 17:45:32 +02:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2020-05-08 16:36:09 +02:00
|
|
|
}
|
|
|
|
|
2021-08-11 21:42:31 +02:00
|
|
|
// CreateDefaultFixtures creates default database entries for test and production.
|
2020-05-08 19:34:29 +02:00
|
|
|
func CreateDefaultFixtures() {
|
2020-09-07 12:18:12 +02:00
|
|
|
CreateUnknownAddress()
|
2020-06-25 01:20:58 +02:00
|
|
|
CreateDefaultUsers()
|
2020-04-30 20:07:03 +02:00
|
|
|
CreateUnknownPlace()
|
2020-07-12 04:48:17 +02:00
|
|
|
CreateUnknownLocation()
|
2020-04-30 20:07:03 +02:00
|
|
|
CreateUnknownCountry()
|
|
|
|
CreateUnknownCamera()
|
|
|
|
CreateUnknownLens()
|
2021-08-11 21:42:31 +02:00
|
|
|
CreateUnknownPerson()
|
2021-08-14 18:13:03 +02:00
|
|
|
CreateUnknownFace()
|
2020-04-30 14:32:48 +02:00
|
|
|
}
|
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
// MigrateDb creates all tables and inserts default entities as needed.
|
|
|
|
func MigrateDb() {
|
|
|
|
Entities.Migrate()
|
|
|
|
Entities.WaitForMigration()
|
2020-05-08 18:18:19 +02:00
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
CreateDefaultFixtures()
|
2020-04-30 14:32:48 +02:00
|
|
|
}
|
2020-04-30 21:21:09 +02:00
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
// ResetTestFixtures drops database tables for all known entities and re-creates them with fixtures.
|
|
|
|
func ResetTestFixtures() {
|
|
|
|
Entities.Migrate()
|
|
|
|
Entities.WaitForMigration()
|
|
|
|
Entities.Truncate()
|
2020-04-30 21:21:09 +02:00
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
CreateDefaultFixtures()
|
2020-04-30 21:21:09 +02:00
|
|
|
|
2020-05-08 19:34:29 +02:00
|
|
|
CreateTestFixtures()
|
2020-05-08 15:41:01 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 21:21:09 +02:00
|
|
|
// InitTestDb connects to and completely initializes the test database incl fixtures.
|
2020-05-30 14:52:47 +02:00
|
|
|
func InitTestDb(driver, dsn string) *Gorm {
|
2020-05-08 15:41:01 +02:00
|
|
|
if HasDbProvider() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-30 14:52:47 +02:00
|
|
|
if driver == "test" || driver == "sqlite" || driver == "" || dsn == "" {
|
|
|
|
driver = "sqlite3"
|
|
|
|
dsn = ".test.db"
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Infof("initializing %s test db in %s", driver, dsn)
|
|
|
|
|
2020-04-30 21:21:09 +02:00
|
|
|
db := &Gorm{
|
2020-05-30 14:52:47 +02:00
|
|
|
Driver: driver,
|
2020-04-30 21:21:09 +02:00
|
|
|
Dsn: dsn,
|
|
|
|
}
|
|
|
|
|
|
|
|
SetDbProvider(db)
|
2020-05-08 19:34:29 +02:00
|
|
|
ResetTestFixtures()
|
2020-04-30 21:21:09 +02:00
|
|
|
|
|
|
|
return db
|
|
|
|
}
|