mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
* 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
164 lines
4.2 KiB
Go
164 lines
4.2 KiB
Go
package exp
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
|
|
"github.com/doug-martin/goqu/v9/internal/errors"
|
|
"github.com/doug-martin/goqu/v9/internal/util"
|
|
)
|
|
|
|
type (
|
|
insert struct {
|
|
from AppendableExpression
|
|
cols ColumnListExpression
|
|
vals [][]interface{}
|
|
}
|
|
)
|
|
|
|
func NewInsertExpression(rows ...interface{}) (insertExpression InsertExpression, err error) {
|
|
switch len(rows) {
|
|
case 0:
|
|
return new(insert), nil
|
|
case 1:
|
|
val := reflect.ValueOf(rows[0])
|
|
if val.Kind() == reflect.Slice {
|
|
vals := make([]interface{}, 0, val.Len())
|
|
for i := 0; i < val.Len(); i++ {
|
|
vals = append(vals, val.Index(i).Interface())
|
|
}
|
|
return NewInsertExpression(vals...)
|
|
}
|
|
if ae, ok := rows[0].(AppendableExpression); ok {
|
|
return &insert{from: ae}, nil
|
|
}
|
|
}
|
|
return newInsert(rows...)
|
|
}
|
|
|
|
func (i *insert) Expression() Expression {
|
|
return i
|
|
}
|
|
|
|
func (i *insert) Clone() Expression {
|
|
return i.clone()
|
|
}
|
|
|
|
func (i *insert) clone() *insert {
|
|
return &insert{from: i.from, cols: i.cols, vals: i.vals}
|
|
}
|
|
|
|
func (i *insert) IsEmpty() bool {
|
|
return i.from == nil && (i.cols == nil || i.cols.IsEmpty())
|
|
}
|
|
|
|
func (i *insert) IsInsertFrom() bool {
|
|
return i.from != nil
|
|
}
|
|
|
|
func (i *insert) From() AppendableExpression {
|
|
return i.from
|
|
}
|
|
|
|
func (i *insert) Cols() ColumnListExpression {
|
|
return i.cols
|
|
}
|
|
|
|
func (i *insert) SetCols(cols ColumnListExpression) InsertExpression {
|
|
ci := i.clone()
|
|
ci.cols = cols
|
|
return ci
|
|
}
|
|
|
|
func (i *insert) Vals() [][]interface{} {
|
|
return i.vals
|
|
}
|
|
|
|
func (i *insert) SetVals(vals [][]interface{}) InsertExpression {
|
|
ci := i.clone()
|
|
ci.vals = vals
|
|
return ci
|
|
}
|
|
|
|
// parses the rows gathering and sorting unique columns and values for each record
|
|
func newInsert(rows ...interface{}) (insertExp InsertExpression, err error) {
|
|
var mapKeys util.ValueSlice
|
|
rowValue := reflect.Indirect(reflect.ValueOf(rows[0]))
|
|
rowType := rowValue.Type()
|
|
rowKind := rowValue.Kind()
|
|
if rowKind == reflect.Struct {
|
|
return createStructSliceInsert(rows...)
|
|
}
|
|
vals := make([][]interface{}, 0, len(rows))
|
|
var columns ColumnListExpression
|
|
for _, row := range rows {
|
|
if rowType != reflect.Indirect(reflect.ValueOf(row)).Type() {
|
|
return nil, errors.New(
|
|
"rows must be all the same type expected %+v got %+v",
|
|
rowType,
|
|
reflect.Indirect(reflect.ValueOf(row)).Type(),
|
|
)
|
|
}
|
|
newRowValue := reflect.Indirect(reflect.ValueOf(row))
|
|
switch rowKind {
|
|
case reflect.Map:
|
|
if columns == nil {
|
|
mapKeys = util.ValueSlice(newRowValue.MapKeys())
|
|
sort.Sort(mapKeys)
|
|
colKeys := make([]interface{}, 0, len(mapKeys))
|
|
for _, key := range mapKeys {
|
|
colKeys = append(colKeys, key.Interface())
|
|
}
|
|
columns = NewColumnListExpression(colKeys...)
|
|
}
|
|
newMapKeys := util.ValueSlice(newRowValue.MapKeys())
|
|
if len(newMapKeys) != len(mapKeys) {
|
|
return nil, errors.New("rows with different value length expected %d got %d", len(mapKeys), len(newMapKeys))
|
|
}
|
|
if !mapKeys.Equal(newMapKeys) {
|
|
return nil, errors.New("rows with different keys expected %s got %s", mapKeys.String(), newMapKeys.String())
|
|
}
|
|
rowVals := make([]interface{}, 0, len(mapKeys))
|
|
for _, key := range mapKeys {
|
|
rowVals = append(rowVals, newRowValue.MapIndex(key).Interface())
|
|
}
|
|
vals = append(vals, rowVals)
|
|
default:
|
|
return nil, errors.New(
|
|
"unsupported insert must be map, goqu.Record, or struct type got: %T",
|
|
row,
|
|
)
|
|
}
|
|
}
|
|
return &insert{cols: columns, vals: vals}, nil
|
|
}
|
|
|
|
func createStructSliceInsert(rows ...interface{}) (insertExp InsertExpression, err error) {
|
|
rowValue := reflect.Indirect(reflect.ValueOf(rows[0]))
|
|
rowType := rowValue.Type()
|
|
recordRows := make([]interface{}, 0, len(rows))
|
|
for _, row := range rows {
|
|
if rowType != reflect.Indirect(reflect.ValueOf(row)).Type() {
|
|
return nil, errors.New(
|
|
"rows must be all the same type expected %+v got %+v",
|
|
rowType,
|
|
reflect.Indirect(reflect.ValueOf(row)).Type(),
|
|
)
|
|
}
|
|
newRowValue := reflect.Indirect(reflect.ValueOf(row))
|
|
record, err := getFieldsValuesFromStruct(newRowValue)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
recordRows = append(recordRows, record)
|
|
}
|
|
return newInsert(recordRows...)
|
|
}
|
|
|
|
func getFieldsValuesFromStruct(value reflect.Value) (row Record, err error) {
|
|
if value.IsValid() {
|
|
return NewRecordFromStruct(value.Interface(), true, false)
|
|
}
|
|
return
|
|
}
|