package db import ( "database/sql" "errors" "fmt" ) // DeleteManifest deletes the manifest with the given digest from a repository. // ON DELETE CASCADE handles manifest_blobs and tags rows. func (d *DB) DeleteManifest(repoID int64, digest string) error { result, err := d.Exec( `DELETE FROM manifests WHERE repository_id = ? AND digest = ?`, repoID, digest, ) if err != nil { return fmt.Errorf("db: delete manifest: %w", err) } n, err := result.RowsAffected() if err != nil { return fmt.Errorf("db: delete manifest rows affected: %w", err) } if n == 0 { return ErrManifestNotFound } return nil } // DeleteBlobFromRepo removes the manifest_blobs associations for a blob // in the given repository. It does NOT delete the blob row or file — that // is GC's job, since other repositories may reference the same blob. // Returns ErrBlobNotFound if the blob is not referenced in this repo. func (d *DB) DeleteBlobFromRepo(repoID int64, digest string) error { // First check that the blob exists and is in this repo. var blobID int64 err := d.QueryRow(`SELECT id FROM blobs WHERE digest = ?`, digest).Scan(&blobID) if err != nil { if errors.Is(err, sql.ErrNoRows) { return ErrBlobNotFound } return fmt.Errorf("db: find blob: %w", err) } result, err := d.Exec( `DELETE FROM manifest_blobs WHERE blob_id = ? AND manifest_id IN ( SELECT id FROM manifests WHERE repository_id = ? )`, blobID, repoID, ) if err != nil { return fmt.Errorf("db: delete blob from repo: %w", err) } n, err := result.RowsAffected() if err != nil { return fmt.Errorf("db: delete blob from repo rows affected: %w", err) } if n == 0 { return ErrBlobNotFound } return nil }