diff --git a/pkg/sqlite/scene_test.go b/pkg/sqlite/scene_test.go index 058def00a..2e4dda8b3 100644 --- a/pkg/sqlite/scene_test.go +++ b/pkg/sqlite/scene_test.go @@ -739,6 +739,22 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) { }, false, }, + { + "add identical galleries", + sceneIDs[sceneIdxWithGallery], + models.ScenePartial{ + GalleryIDs: &models.UpdateIDs{ + IDs: []int{galleryIDs[galleryIdx1WithImage], galleryIDs[galleryIdx1WithImage]}, + Mode: models.RelationshipUpdateModeAdd, + }, + }, + models.Scene{ + GalleryIDs: models.NewRelatedIDs(append(indexesToIDs(galleryIDs, sceneGalleries[sceneIdxWithGallery]), + galleryIDs[galleryIdx1WithImage], + )), + }, + false, + }, { "add tags", sceneIDs[sceneIdxWithTwoTags], @@ -759,6 +775,25 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) { }, false, }, + { + "add identical tags", + sceneIDs[sceneIdxWithTwoTags], + models.ScenePartial{ + TagIDs: &models.UpdateIDs{ + IDs: []int{tagIDs[tagIdx1WithDupName], tagIDs[tagIdx1WithDupName]}, + Mode: models.RelationshipUpdateModeAdd, + }, + }, + models.Scene{ + TagIDs: models.NewRelatedIDs(append( + []int{ + tagIDs[tagIdx1WithDupName], + }, + indexesToIDs(tagIDs, sceneTags[sceneIdxWithTwoTags])..., + )), + }, + false, + }, { "add performers", sceneIDs[sceneIdxWithTwoPerformers], @@ -776,6 +811,22 @@ func Test_sceneQueryBuilder_UpdatePartialRelationships(t *testing.T) { }, false, }, + { + "add identical performers", + sceneIDs[sceneIdxWithTwoPerformers], + models.ScenePartial{ + PerformerIDs: &models.UpdateIDs{ + IDs: []int{performerIDs[performerIdx1WithDupName], performerIDs[performerIdx1WithDupName]}, + Mode: models.RelationshipUpdateModeAdd, + }, + }, + models.Scene{ + PerformerIDs: models.NewRelatedIDs(append(indexesToIDs(performerIDs, scenePerformers[sceneIdxWithTwoPerformers]), + performerIDs[performerIdx1WithDupName], + )), + }, + false, + }, { "add movies", sceneIDs[sceneIdxWithMovie], diff --git a/pkg/sqlite/table.go b/pkg/sqlite/table.go index 438d1e7b8..fbb3bbb89 100644 --- a/pkg/sqlite/table.go +++ b/pkg/sqlite/table.go @@ -179,21 +179,23 @@ func (t *joinTable) get(ctx context.Context, id int) ([]int, error) { return ret, nil } -func (t *joinTable) insertJoin(ctx context.Context, id, foreignID int) (sql.Result, error) { - q := dialect.Insert(t.table.table).Cols(t.idColumn.GetCol(), t.fkColumn.GetCol()).Vals( - goqu.Vals{id, foreignID}, - ) - ret, err := exec(ctx, q) - if err != nil { - return nil, fmt.Errorf("inserting into %s: %w", t.table.table.GetTable(), err) - } - - return ret, nil -} - func (t *joinTable) insertJoins(ctx context.Context, id int, foreignIDs []int) error { + // manually create SQL so that we can prepare once + // ignore duplicates + q := fmt.Sprintf("INSERT INTO %s (%s, %s) VALUES (?, ?) ON CONFLICT (%[2]s, %s) DO NOTHING", t.table.table.GetTable(), t.idColumn.GetCol(), t.fkColumn.GetCol()) + + tx := dbWrapper{} + stmt, err := tx.Prepare(ctx, q) + if err != nil { + return err + } + defer stmt.Close() + + // eliminate duplicates + foreignIDs = intslice.IntAppendUniques(nil, foreignIDs) + for _, fk := range foreignIDs { - if _, err := t.insertJoin(ctx, id, fk); err != nil { + if _, err := tx.ExecStmt(ctx, stmt, id, fk); err != nil { return err } }