add Backup.Remaining and Backup.PageCount progress wrappers
Two thin wrappers around the existing sqlite3_backup_remaining and sqlite3_backup_pagecount C symbols. They expose the underlying backup progress counters that the SQLite C layer already tracks but that Go callers cannot currently read without dropping to lib/* directly.
Motivation
Standard progress-UI use case for online backups:
for {
more, err := bck.Step(pagesPerTick)
if err != nil {
return err
}
ui.Update(bck.PageCount()-bck.Remaining(), bck.PageCount())
if !more {
break
}
}Without these wrappers a caller has to either skip the progress display or fall back to per-call SQL queries against pragma_page_count, which is racy against an in-flight backup.
API shape
Mirrors !115 (merged) (FileControlDataVersion): the wrapper takes the SQLite C function name with the sqlite3_ prefix stripped and CamelCase applied, documented inline with a link to the official C API page, added on the existing public *Backup receiver. No new interface, no new escape hatch.
The C functions are zero-arg lookups against the sqlite3_backup object and cannot fail, so the Go wrappers return int with no error:
func (b *Backup) Remaining() int
func (b *Backup) PageCount() intBehavior
Per the SQLite docs:
- Before the first
Step, both methods return 0. - After a partial
Step,PageCountis the source database page count andRemainingisPageCount - copied. - After the final
StepreturnsSQLITE_DONE,Remainingdrops to 0 andPageCountkeeps reporting the source page count.
Tests
TestBackupProgress exercises all three phases on an in-memory source populated to span multiple pages, then drives a partial Step (Step(1)) followed by Step(-1). It asserts:
- Both counters are 0 before any Step.
- After the partial Step:
PageCount > 1,Remaining > 0,Remaining == PageCount - 1. - After
SQLITE_DONE:Remaining == 0,PageCountunchanged from mid-backup.
Full go test -count=1 ./... stays green (467s on darwin/arm64).