All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
78 lines
2.0 KiB
Go
78 lines
2.0 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.wntrmute.dev/mc/mcr/internal/gc"
|
|
)
|
|
|
|
// FindAndDeleteUnreferencedBlobs finds all blob rows with no manifest_blobs
|
|
// entries, deletes them in a single transaction, and returns the digests
|
|
// and sizes of the deleted blobs.
|
|
func (d *DB) FindAndDeleteUnreferencedBlobs() ([]gc.UnreferencedBlob, error) {
|
|
tx, err := d.Begin()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("db: begin gc transaction: %w", err)
|
|
}
|
|
|
|
// Find unreferenced blobs.
|
|
rows, err := tx.Query(
|
|
`SELECT b.id, b.digest, b.size FROM blobs b
|
|
LEFT JOIN manifest_blobs mb ON mb.blob_id = b.id
|
|
WHERE mb.manifest_id IS NULL`,
|
|
)
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
return nil, fmt.Errorf("db: find unreferenced blobs: %w", err)
|
|
}
|
|
|
|
var unreferenced []gc.UnreferencedBlob
|
|
var ids []int64
|
|
for rows.Next() {
|
|
var id int64
|
|
var blob gc.UnreferencedBlob
|
|
if err := rows.Scan(&id, &blob.Digest, &blob.Size); err != nil {
|
|
_ = rows.Close()
|
|
_ = tx.Rollback()
|
|
return nil, fmt.Errorf("db: scan unreferenced blob: %w", err)
|
|
}
|
|
unreferenced = append(unreferenced, blob)
|
|
ids = append(ids, id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
_ = rows.Close()
|
|
_ = tx.Rollback()
|
|
return nil, fmt.Errorf("db: iterate unreferenced blobs: %w", err)
|
|
}
|
|
_ = rows.Close()
|
|
|
|
// Delete the unreferenced blob rows.
|
|
for _, id := range ids {
|
|
if _, err := tx.Exec(`DELETE FROM blobs WHERE id = ?`, id); err != nil {
|
|
_ = tx.Rollback()
|
|
return nil, fmt.Errorf("db: delete blob %d: %w", id, err)
|
|
}
|
|
}
|
|
|
|
if err := tx.Commit(); err != nil {
|
|
return nil, fmt.Errorf("db: commit gc transaction: %w", err)
|
|
}
|
|
|
|
return unreferenced, nil
|
|
}
|
|
|
|
// BlobExistsByDigest checks whether a blob row exists for the given digest.
|
|
func (d *DB) BlobExistsByDigest(digest string) (bool, error) {
|
|
var count int
|
|
err := d.QueryRow(`SELECT COUNT(*) FROM blobs WHERE digest = ?`, digest).Scan(&count)
|
|
if err != nil {
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return false, nil
|
|
}
|
|
return false, fmt.Errorf("db: blob exists by digest: %w", err)
|
|
}
|
|
return count > 0, nil
|
|
}
|