check / check (push) Waiting to run
WithReadTx ended the process when its rollback failed, including after Ctrl-C, when database/sql has already rolled back and Rollback returns sql.ErrTxDone; it now handles the rollback as WithTx does. The query functions ended the process when closing their rows failed; they now leave the deferred close alone, since database/sql reports a close failure through rows.Err(), which each returns. The unused database.Fatalf is removed. cleanupBlobTempFile now returns its close or remove failure, which fails the backup. No test makes a rows close fail: SQLite fails it only with the error the last read already returned. Model: opus-5-5
61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
package database
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// List returns every chunk in the index, ordered by chunk hash.
|
|
func (r *ChunkRepository) List(ctx context.Context) ([]*Chunk, error) {
|
|
return r.list(ctx, `
|
|
SELECT chunk_hash, size
|
|
FROM chunks
|
|
ORDER BY chunk_hash
|
|
`)
|
|
}
|
|
|
|
// ListInUploadedBlobs returns the chunks that are stored in a blob whose
|
|
// upload has completed (uploaded_ts set), ordered by chunk hash. These
|
|
// are the only chunks a backup may safely deduplicate against: a chunk
|
|
// recorded solely in a blob that was never uploaded refers to data that
|
|
// is not in remote storage, so trusting it would silently drop that data
|
|
// from later snapshots.
|
|
func (r *ChunkRepository) ListInUploadedBlobs(ctx context.Context) ([]*Chunk, error) {
|
|
return r.list(ctx, `
|
|
SELECT DISTINCT c.chunk_hash, c.size
|
|
FROM chunks c
|
|
JOIN blob_chunks bc ON c.chunk_hash = bc.chunk_hash
|
|
JOIN blobs b ON bc.blob_id = b.id
|
|
WHERE b.uploaded_ts IS NOT NULL
|
|
ORDER BY c.chunk_hash
|
|
`)
|
|
}
|
|
|
|
// list runs a chunk-selecting query and scans the (chunk_hash, size) rows.
|
|
func (r *ChunkRepository) list(ctx context.Context, query string) ([]*Chunk, error) {
|
|
rows, err := r.db.conn.QueryContext(ctx, query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("querying chunks: %w", err)
|
|
}
|
|
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var chunks []*Chunk
|
|
|
|
for rows.Next() {
|
|
var chunk Chunk
|
|
|
|
err := rows.Scan(
|
|
&chunk.ChunkHash,
|
|
&chunk.Size,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("scanning chunk: %w", err)
|
|
}
|
|
|
|
chunks = append(chunks, &chunk)
|
|
}
|
|
|
|
return chunks, rows.Err()
|
|
}
|