mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
File storage rewrite (#2676)
* Restructure data layer part 2 (#2599) * Refactor and separate image model * Refactor image query builder * Handle relationships in image query builder * Remove relationship management methods * Refactor gallery model/query builder * Add scenes to gallery model * Convert scene model * Refactor scene models * Remove unused methods * Add unit tests for gallery * Add image tests * Add scene tests * Convert unnecessary scene value pointers to values * Convert unnecessary pointer values to values * Refactor scene partial * Add scene partial tests * Refactor ImagePartial * Add image partial tests * Refactor gallery partial update * Add partial gallery update tests * Use zero/null package for null values * Add files and scan system * Add sqlite implementation for files/folders * Add unit tests for files/folders * Image refactors * Update image data layer * Refactor gallery model and creation * Refactor scene model * Refactor scenes * Don't set title from filename * Allow galleries to freely add/remove images * Add multiple scene file support to graphql and UI * Add multiple file support for images in graphql/UI * Add multiple file for galleries in graphql/UI * Remove use of some deprecated fields * Remove scene path usage * Remove gallery path usage * Remove path from image * Move funscript to video file * Refactor caption detection * Migrate existing data * Add post commit/rollback hook system * Lint. Comment out import/export tests * Add WithDatabase read only wrapper * Prepend tasks to list * Add 32 pre-migration * Add warnings in release and migration notes
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/stashapp/stash/pkg/file"
|
||||
"github.com/stashapp/stash/pkg/logger"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
@@ -22,7 +23,7 @@ type objectList interface {
|
||||
}
|
||||
|
||||
type repository struct {
|
||||
tx dbi
|
||||
tx dbWrapper
|
||||
tableName string
|
||||
idColumn string
|
||||
}
|
||||
@@ -32,11 +33,6 @@ func (r *repository) getByID(ctx context.Context, id int, dest interface{}) erro
|
||||
return r.tx.Get(ctx, dest, stmt, id)
|
||||
}
|
||||
|
||||
func (r *repository) getAll(ctx context.Context, id int, f func(rows *sqlx.Rows) error) error {
|
||||
stmt := fmt.Sprintf("SELECT * FROM %s WHERE %s = ?", r.tableName, r.idColumn)
|
||||
return r.queryFunc(ctx, stmt, []interface{}{id}, false, f)
|
||||
}
|
||||
|
||||
func (r *repository) insert(ctx context.Context, obj interface{}) (sql.Result, error) {
|
||||
stmt := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", r.tableName, listKeys(obj, false), listKeys(obj, true))
|
||||
return r.tx.NamedExec(ctx, stmt, obj)
|
||||
@@ -70,21 +66,21 @@ func (r *repository) update(ctx context.Context, id int, obj interface{}, partia
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *repository) updateMap(ctx context.Context, id int, m map[string]interface{}) error {
|
||||
exists, err := r.exists(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// func (r *repository) updateMap(ctx context.Context, id int, m map[string]interface{}) error {
|
||||
// exists, err := r.exists(ctx, id)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if !exists {
|
||||
return fmt.Errorf("%s %d does not exist in %s", r.idColumn, id, r.tableName)
|
||||
}
|
||||
// if !exists {
|
||||
// return fmt.Errorf("%s %d does not exist in %s", r.idColumn, id, r.tableName)
|
||||
// }
|
||||
|
||||
stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s.%s = :id", r.tableName, updateSetMap(m), r.tableName, r.idColumn)
|
||||
_, err = r.tx.NamedExec(ctx, stmt, m)
|
||||
// stmt := fmt.Sprintf("UPDATE %s SET %s WHERE %s.%s = :id", r.tableName, updateSetMap(m), r.tableName, r.idColumn)
|
||||
// _, err = r.tx.NamedExec(ctx, stmt, m)
|
||||
|
||||
return err
|
||||
}
|
||||
// return err
|
||||
// }
|
||||
|
||||
func (r *repository) destroyExisting(ctx context.Context, ids []int) error {
|
||||
for _, id := range ids {
|
||||
@@ -147,7 +143,7 @@ func (r *repository) runIdsQuery(ctx context.Context, query string, args []inter
|
||||
}
|
||||
|
||||
if err := r.tx.Select(ctx, &result, query, args...); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return []int{}, err
|
||||
return []int{}, fmt.Errorf("running query: %s [%v]: %w", query, args, err)
|
||||
}
|
||||
|
||||
vsm := make([]int, len(result))
|
||||
@@ -157,20 +153,6 @@ func (r *repository) runIdsQuery(ctx context.Context, query string, args []inter
|
||||
return vsm, nil
|
||||
}
|
||||
|
||||
func (r *repository) runSumQuery(ctx context.Context, query string, args []interface{}) (float64, error) {
|
||||
// Perform query and fetch result
|
||||
result := struct {
|
||||
Float64 float64 `db:"sum"`
|
||||
}{0}
|
||||
|
||||
// Perform query and fetch result
|
||||
if err := r.tx.Get(ctx, &result, query, args...); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return result.Float64, nil
|
||||
}
|
||||
|
||||
func (r *repository) queryFunc(ctx context.Context, query string, args []interface{}, single bool, f func(rows *sqlx.Rows) error) error {
|
||||
logger.Tracef("SQL: %s, args: %v", query, args)
|
||||
|
||||
@@ -209,12 +191,16 @@ func (r *repository) query(ctx context.Context, query string, args []interface{}
|
||||
}
|
||||
|
||||
func (r *repository) queryStruct(ctx context.Context, query string, args []interface{}, out interface{}) error {
|
||||
return r.queryFunc(ctx, query, args, true, func(rows *sqlx.Rows) error {
|
||||
if err := r.queryFunc(ctx, query, args, true, func(rows *sqlx.Rows) error {
|
||||
if err := rows.StructScan(out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}); err != nil {
|
||||
return fmt.Errorf("executing query: %s [%v]: %w", query, args, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *repository) querySimple(ctx context.Context, query string, args []interface{}, out interface{}) error {
|
||||
@@ -370,9 +356,9 @@ type captionRepository struct {
|
||||
repository
|
||||
}
|
||||
|
||||
func (r *captionRepository) get(ctx context.Context, id int) ([]*models.SceneCaption, error) {
|
||||
query := fmt.Sprintf("SELECT %s, %s, %s from %s WHERE %s = ?", sceneCaptionCodeColumn, sceneCaptionFilenameColumn, sceneCaptionTypeColumn, r.tableName, r.idColumn)
|
||||
var ret []*models.SceneCaption
|
||||
func (r *captionRepository) get(ctx context.Context, id file.ID) ([]*models.VideoCaption, error) {
|
||||
query := fmt.Sprintf("SELECT %s, %s, %s from %s WHERE %s = ?", captionCodeColumn, captionFilenameColumn, captionTypeColumn, r.tableName, r.idColumn)
|
||||
var ret []*models.VideoCaption
|
||||
err := r.queryFunc(ctx, query, []interface{}{id}, false, func(rows *sqlx.Rows) error {
|
||||
var captionCode string
|
||||
var captionFilename string
|
||||
@@ -382,7 +368,7 @@ func (r *captionRepository) get(ctx context.Context, id int) ([]*models.SceneCap
|
||||
return err
|
||||
}
|
||||
|
||||
caption := &models.SceneCaption{
|
||||
caption := &models.VideoCaption{
|
||||
LanguageCode: captionCode,
|
||||
Filename: captionFilename,
|
||||
CaptionType: captionType,
|
||||
@@ -393,13 +379,13 @@ func (r *captionRepository) get(ctx context.Context, id int) ([]*models.SceneCap
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (r *captionRepository) insert(ctx context.Context, id int, caption *models.SceneCaption) (sql.Result, error) {
|
||||
stmt := fmt.Sprintf("INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, ?)", r.tableName, r.idColumn, sceneCaptionCodeColumn, sceneCaptionFilenameColumn, sceneCaptionTypeColumn)
|
||||
func (r *captionRepository) insert(ctx context.Context, id file.ID, caption *models.VideoCaption) (sql.Result, error) {
|
||||
stmt := fmt.Sprintf("INSERT INTO %s (%s, %s, %s, %s) VALUES (?, ?, ?, ?)", r.tableName, r.idColumn, captionCodeColumn, captionFilenameColumn, captionTypeColumn)
|
||||
return r.tx.Exec(ctx, stmt, id, caption.LanguageCode, caption.Filename, caption.CaptionType)
|
||||
}
|
||||
|
||||
func (r *captionRepository) replace(ctx context.Context, id int, captions []*models.SceneCaption) error {
|
||||
if err := r.destroy(ctx, []int{id}); err != nil {
|
||||
func (r *captionRepository) replace(ctx context.Context, id file.ID, captions []*models.VideoCaption) error {
|
||||
if err := r.destroy(ctx, []int{int(id)}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -472,7 +458,7 @@ func (r *stashIDRepository) get(ctx context.Context, id int) ([]*models.StashID,
|
||||
return []*models.StashID(ret), err
|
||||
}
|
||||
|
||||
func (r *stashIDRepository) replace(ctx context.Context, id int, newIDs []models.StashID) error {
|
||||
func (r *stashIDRepository) replace(ctx context.Context, id int, newIDs []*models.StashID) error {
|
||||
if err := r.destroy(ctx, []int{id}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -529,10 +515,10 @@ func updateSet(i interface{}, partial bool) string {
|
||||
return strings.Join(query, ", ")
|
||||
}
|
||||
|
||||
func updateSetMap(m map[string]interface{}) string {
|
||||
var query []string
|
||||
for k := range m {
|
||||
query = append(query, fmt.Sprintf("%s=:%s", k, k))
|
||||
}
|
||||
return strings.Join(query, ", ")
|
||||
}
|
||||
// func updateSetMap(m map[string]interface{}) string {
|
||||
// var query []string
|
||||
// for k := range m {
|
||||
// query = append(query, fmt.Sprintf("%s=:%s", k, k))
|
||||
// }
|
||||
// return strings.Join(query, ", ")
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user