Restructure data layer (#2532)

* Add new txn manager interface
* Add txn management to sqlite
* Rename get to getByID
* Add contexts to repository methods
* Update query builders
* Add context to reader writer interfaces
* Use repository in resolver
* Tighten interfaces
* Tighten interfaces in dlna
* Tighten interfaces in match package
* Tighten interfaces in scraper package
* Tighten interfaces in scan code
* Tighten interfaces on autotag package
* Remove ReaderWriter usage
* Merge database package into sqlite
This commit is contained in:
WithoutPants
2022-05-19 17:49:32 +10:00
parent 7b5bd80515
commit 964b559309
244 changed files with 7377 additions and 6699 deletions

View File

@@ -2,209 +2,67 @@ package sqlite
import (
"context"
"database/sql"
"errors"
"fmt"
"github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/models"
)
type dbi interface {
Get(dest interface{}, query string, args ...interface{}) error
Select(dest interface{}, query string, args ...interface{}) error
Queryx(query string, args ...interface{}) (*sqlx.Rows, error)
NamedExec(query string, arg interface{}) (sql.Result, error)
Exec(query string, args ...interface{}) (sql.Result, error)
}
type key int
type transaction struct {
Ctx context.Context
tx *sqlx.Tx
}
const (
txnKey key = iota + 1
)
func (t *transaction) Begin() error {
if t.tx != nil {
return errors.New("transaction already begun")
func (db *Database) Begin(ctx context.Context) (context.Context, error) {
if tx, _ := getTx(ctx); tx != nil {
return nil, fmt.Errorf("already in transaction")
}
if err := database.Ready(); err != nil {
tx, err := db.db.BeginTxx(ctx, nil)
if err != nil {
return nil, fmt.Errorf("beginning transaction: %w", err)
}
return context.WithValue(ctx, txnKey, tx), nil
}
func (db *Database) Commit(ctx context.Context) error {
tx, err := getTx(ctx)
if err != nil {
return err
}
return tx.Commit()
}
var err error
t.tx, err = database.DB.BeginTxx(t.Ctx, nil)
func (db *Database) Rollback(ctx context.Context) error {
tx, err := getTx(ctx)
if err != nil {
return fmt.Errorf("error starting transaction: %v", err)
}
return nil
}
func (t *transaction) Rollback() error {
if t.tx == nil {
return errors.New("not in transaction")
}
err := t.tx.Rollback()
if err != nil {
return fmt.Errorf("error rolling back transaction: %v", err)
}
t.tx = nil
return nil
}
func (t *transaction) Commit() error {
if t.tx == nil {
return errors.New("not in transaction")
}
err := t.tx.Commit()
if err != nil {
return fmt.Errorf("error committing transaction: %v", err)
}
t.tx = nil
return nil
}
func (t *transaction) Repository() models.Repository {
return t
}
func (t *transaction) ensureTx() {
if t.tx == nil {
panic("tx is nil")
}
}
func (t *transaction) Gallery() models.GalleryReaderWriter {
t.ensureTx()
return NewGalleryReaderWriter(t.tx)
}
func (t *transaction) Image() models.ImageReaderWriter {
t.ensureTx()
return NewImageReaderWriter(t.tx)
}
func (t *transaction) Movie() models.MovieReaderWriter {
t.ensureTx()
return NewMovieReaderWriter(t.tx)
}
func (t *transaction) Performer() models.PerformerReaderWriter {
t.ensureTx()
return NewPerformerReaderWriter(t.tx)
}
func (t *transaction) SceneMarker() models.SceneMarkerReaderWriter {
t.ensureTx()
return NewSceneMarkerReaderWriter(t.tx)
}
func (t *transaction) Scene() models.SceneReaderWriter {
t.ensureTx()
return NewSceneReaderWriter(t.tx)
}
func (t *transaction) ScrapedItem() models.ScrapedItemReaderWriter {
t.ensureTx()
return NewScrapedItemReaderWriter(t.tx)
}
func (t *transaction) Studio() models.StudioReaderWriter {
t.ensureTx()
return NewStudioReaderWriter(t.tx)
}
func (t *transaction) Tag() models.TagReaderWriter {
t.ensureTx()
return NewTagReaderWriter(t.tx)
}
func (t *transaction) SavedFilter() models.SavedFilterReaderWriter {
t.ensureTx()
return NewSavedFilterReaderWriter(t.tx)
}
type ReadTransaction struct{}
func (t *ReadTransaction) Begin() error {
if err := database.Ready(); err != nil {
return err
}
return nil
return tx.Rollback()
}
func (t *ReadTransaction) Rollback() error {
return nil
func getTx(ctx context.Context) (*sqlx.Tx, error) {
tx, ok := ctx.Value(txnKey).(*sqlx.Tx)
if !ok || tx == nil {
return nil, fmt.Errorf("not in transaction")
}
return tx, nil
}
func (t *ReadTransaction) Commit() error {
return nil
}
func (t *ReadTransaction) Repository() models.ReaderRepository {
return t
}
func (t *ReadTransaction) Gallery() models.GalleryReader {
return NewGalleryReaderWriter(database.DB)
}
func (t *ReadTransaction) Image() models.ImageReader {
return NewImageReaderWriter(database.DB)
}
func (t *ReadTransaction) Movie() models.MovieReader {
return NewMovieReaderWriter(database.DB)
}
func (t *ReadTransaction) Performer() models.PerformerReader {
return NewPerformerReaderWriter(database.DB)
}
func (t *ReadTransaction) SceneMarker() models.SceneMarkerReader {
return NewSceneMarkerReaderWriter(database.DB)
}
func (t *ReadTransaction) Scene() models.SceneReader {
return NewSceneReaderWriter(database.DB)
}
func (t *ReadTransaction) ScrapedItem() models.ScrapedItemReader {
return NewScrapedItemReaderWriter(database.DB)
}
func (t *ReadTransaction) Studio() models.StudioReader {
return NewStudioReaderWriter(database.DB)
}
func (t *ReadTransaction) Tag() models.TagReader {
return NewTagReaderWriter(database.DB)
}
func (t *ReadTransaction) SavedFilter() models.SavedFilterReader {
return NewSavedFilterReaderWriter(database.DB)
}
type TransactionManager struct {
}
func NewTransactionManager() *TransactionManager {
return &TransactionManager{}
}
func (t *TransactionManager) WithTxn(ctx context.Context, fn func(r models.Repository) error) error {
database.WriteMu.Lock()
defer database.WriteMu.Unlock()
return models.WithTxn(&transaction{Ctx: ctx}, fn)
}
func (t *TransactionManager) WithReadTxn(ctx context.Context, fn func(r models.ReaderRepository) error) error {
return models.WithROTxn(&ReadTransaction{}, fn)
func (db *Database) TxnRepository() models.Repository {
return models.Repository{
TxnManager: db,
Gallery: GalleryReaderWriter,
Image: ImageReaderWriter,
Movie: MovieReaderWriter,
Performer: PerformerReaderWriter,
Scene: SceneReaderWriter,
SceneMarker: SceneMarkerReaderWriter,
ScrapedItem: ScrapedItemReaderWriter,
Studio: StudioReaderWriter,
Tag: TagReaderWriter,
SavedFilter: SavedFilterReaderWriter,
}
}