mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
|
|
// Package cksmvfs wraps an SQLite VFS to help detect database corruption.
|
||
|
|
//
|
||
|
|
// The "cksmvfs" [vfs.VFS] wraps the default VFS adding an 8-byte checksum
|
||
|
|
// to the end of every page in an SQLite database.
|
||
|
|
// The checksum is added as each page is written
|
||
|
|
// and verified as each page is read.
|
||
|
|
// The checksum is intended to help detect database corruption
|
||
|
|
// caused by random bit-flips in the mass storage device.
|
||
|
|
//
|
||
|
|
// This implementation is compatible with SQLite's
|
||
|
|
// [Checksum VFS Shim].
|
||
|
|
//
|
||
|
|
// [Checksum VFS Shim]: https://sqlite.org/cksumvfs.html
|
||
|
|
package cksmvfs
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"github.com/ncruces/go-sqlite3"
|
||
|
|
"github.com/ncruces/go-sqlite3/vfs"
|
||
|
|
)
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
vfs.Register("cksmvfs", Wrap(vfs.Find("")))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Wrap wraps a base VFS to create a checksumming VFS.
|
||
|
|
func Wrap(base vfs.VFS) vfs.VFS {
|
||
|
|
return &cksmVFS{VFS: base}
|
||
|
|
}
|
||
|
|
|
||
|
|
// EnableChecksums enables checksums on a database.
|
||
|
|
func EnableChecksums(db *sqlite3.Conn, schema string) error {
|
||
|
|
if f, ok := db.Filename("").DatabaseFile().(*cksmFile); !ok {
|
||
|
|
return fmt.Errorf("cksmvfs: incorrect type: %T", f)
|
||
|
|
}
|
||
|
|
|
||
|
|
r, err := db.FileControl(schema, sqlite3.FCNTL_RESERVE_BYTES)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if r == 8 {
|
||
|
|
// Correct value, enabled.
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
if r == 0 {
|
||
|
|
// Default value, enable.
|
||
|
|
_, err = db.FileControl(schema, sqlite3.FCNTL_RESERVE_BYTES, 8)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
r, err = db.FileControl(schema, sqlite3.FCNTL_RESERVE_BYTES)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if r != 8 {
|
||
|
|
// Invalid value.
|
||
|
|
return fmt.Errorf("cksmvfs: reserve bytes must be 8, is: %d", r)
|
||
|
|
}
|
||
|
|
|
||
|
|
// VACUUM the database.
|
||
|
|
if schema != "" {
|
||
|
|
err = db.Exec(`VACUUM ` + sqlite3.QuoteIdentifier(schema))
|
||
|
|
} else {
|
||
|
|
err = db.Exec(`VACUUM`)
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Checkpoint the WAL.
|
||
|
|
_, _, err = db.WALCheckpoint(schema, sqlite3.CHECKPOINT_RESTART)
|
||
|
|
return err
|
||
|
|
}
|