diff --git a/models/packages/package_file.go b/models/packages/package_file.go index bf877485d6..77cd163b23 100644 --- a/models/packages/package_file.go +++ b/models/packages/package_file.go @@ -115,6 +115,21 @@ func DeleteFileByID(ctx context.Context, fileID int64) error { return err } +// DeleteFilesByPackageID deletes all files of a specific package +// Versions must not be deleted prior to this call +func DeleteFilesByPackageID(ctx context.Context, packageID int64) error { + deleteStmt := builder.Delete(builder.In("version_id", builder.Select("package_version.id").From("package_version").Where(builder.Eq{"package_id": packageID}))).From("package_file") + _, err := db.GetEngine(ctx).Exec(deleteStmt) + return err +} + +// DeleteFilesByVersionID deletes all files of a specific version +func DeleteFilesByVersionID(ctx context.Context, versionID int64) error { + + _, err := db.GetEngine(ctx).Where("version_id = ?", versionID).Delete(&PackageFile{}) + return err +} + func UpdateFile(ctx context.Context, pf *PackageFile, cols []string) error { _, err := db.GetEngine(ctx).ID(pf.ID).Cols(cols...).Update(pf) return err diff --git a/models/packages/package_property.go b/models/packages/package_property.go index acc05d8d5a..e61967c85d 100644 --- a/models/packages/package_property.go +++ b/models/packages/package_property.go @@ -86,6 +86,44 @@ func DeleteAllProperties(ctx context.Context, refType PropertyType, refID int64) return err } +// DeletePropertiesByPackageID deletes properties of a typed linked to the package +// Use to avoid for loops in mass deletion of properties +func DeletePropertiesByPackageID(ctx context.Context, refType PropertyType, packageID int64) error { + var deleteStmt *builder.Builder + + switch refType { + case PropertyTypeFile: + deleteStmt = builder.Delete( + // Delete all properties that are attached to a file and are in ids from a subquery + // which returns ids from the package_file table joined on package_version to link it with package id + builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id", + builder.Select("package_file.id").From("package_file"). + LeftJoin("package_version", "package_file.version_id = package_version.id"). + Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property") + case PropertyTypeVersion: + // Delete all properties that are attached to a version and are in ids from subquery to the package_version filtered by package id + deleteStmt = builder.Delete( + builder.Eq{"ref_type": PropertyTypeVersion}, builder.In("ref_id", + builder.Select("package_version.id").From("package_version"). + Where(builder.Eq{"package_version.package_id": packageID}))).From("package_property") + case PropertyTypePackage: + // Delete all properties that are attached to a package and their reference links to the given package ID + deleteStmt = builder.Delete( + builder.Eq{"ref_type": PropertyTypePackage}, builder.Eq{"ref_id": packageID}). + From("package_property") + } + + _, err := db.GetEngine(ctx).Exec(deleteStmt) + return err +} + +// DeleteFilePropertiesByVersionID deletes all file properties linked to specific version +func DeleteFilePropertiesByVersionID(ctx context.Context, versionID int64) error { + deleteStmt := builder.Delete(builder.Eq{"ref_type": PropertyTypeFile}, builder.In("ref_id", builder.Select("id").From("package_file").Where(builder.Eq{"version_id": versionID}))).From("package_property") + _, err := db.GetEngine(ctx).Exec(deleteStmt) + return err +} + // DeletePropertyByID deletes a property func DeletePropertyByID(ctx context.Context, propertyID int64) error { _, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{}) diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 0a478c0323..3e0e1899ea 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -157,6 +157,12 @@ func DeleteVersionByID(ctx context.Context, versionID int64) error { return err } +// DeleteVersionsByPackageID deletes all versions of a specific package +func DeleteVersionsByPackageID(ctx context.Context, packageID int64) error { + _, err := db.GetEngine(ctx).Where(builder.Eq{"package_id": packageID}).Delete(&PackageVersion{}) + return err +} + // HasVersionFileReferences checks if there are associated files func HasVersionFileReferences(ctx context.Context, versionID int64) (bool, error) { return db.GetEngine(ctx).Get(&PackageFile{ diff --git a/routers/web/user/package.go b/routers/web/user/package.go index ab46f284d3..f59a25d352 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -493,7 +493,7 @@ func packageSettingsPostActionLink(ctx *context.Context, form *forms.PackageSett func packageSettingsPostActionDelete(ctx *context.Context) { pd := ctx.Package.Descriptor - if err := packages_service.RemovePackage(ctx, ctx.Doer, pd.Package); err != nil { + if err := packages_service.RemovePackage(ctx, pd.Package); err != nil { log.Error("Error deleting package: %v", err) ctx.Flash.Error(ctx.Tr("packages.settings.delete.error")) } else { diff --git a/services/packages/packages.go b/services/packages/packages.go index 85269dbe32..16ec7a3598 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -532,16 +532,11 @@ func DeletePackageVersionAndReferences(ctx context.Context, pv *packages_model.P if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypeVersion, pv.ID); err != nil { return err } - - pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID) - if err != nil { + if err := packages_model.DeleteFilePropertiesByVersionID(ctx, pv.ID); err != nil { return err } - - for _, pf := range pfs { - if err := DeletePackageFile(ctx, pf); err != nil { - return err - } + if err := packages_model.DeleteFilesByVersionID(ctx, pv.ID); err != nil { + return err } return packages_model.DeleteVersionByID(ctx, pv.ID) @@ -630,22 +625,31 @@ func OpenBlobForDownload(ctx context.Context, pf *packages_model.PackageFile, pb } // RemovePackage deletes the package and all its versions -func RemovePackage(ctx context.Context, doer *user_model.User, p *packages_model.Package) error { - pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ - PackageID: p.ID, - IsInternal: optional.None[bool](), - }) - if err != nil { - return err - } - - for _, pv := range pvs { - if err := RemovePackageVersion(ctx, doer, pv); err != nil { +func RemovePackage(ctx context.Context, p *packages_model.Package) error { + return db.WithTx(ctx, func(ctx context.Context) error { + err := packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypePackage, p.ID) + if err != nil { + return err + } + err = packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypeFile, p.ID) + if err != nil { + return err + } + err = packages_model.DeletePropertiesByPackageID(ctx, packages_model.PropertyTypeVersion, p.ID) + if err != nil { + return err + } + err = packages_model.DeleteFilesByPackageID(ctx, p.ID) + if err != nil { + return err + } + err = packages_model.DeleteVersionsByPackageID(ctx, p.ID) + if err != nil { return err } - } - return packages_model.DeletePackageByID(ctx, p.ID) + return packages_model.DeletePackageByID(ctx, p.ID) + }) } // RemoveAllPackages for User