diff --git a/cmd/bbolt/main.go b/cmd/bbolt/main.go index 345dfedd0..f76cd82c4 100644 --- a/cmd/bbolt/main.go +++ b/cmd/bbolt/main.go @@ -213,7 +213,7 @@ func (cmd *checkCommand) Run(args ...string) error { // Perform consistency check. return db.View(func(tx *bolt.Tx) error { var count int - for err := range tx.Check(CmdKvStringer()) { + for err := range tx.CheckWithOptions(bolt.WithKVStringer(CmdKvStringer())) { fmt.Fprintln(cmd.Stdout, err) count++ } diff --git a/db_test.go b/db_test.go index d514934df..9f1076fd4 100644 --- a/db_test.go +++ b/db_test.go @@ -398,7 +398,7 @@ func TestOpen_Check(t *testing.T) { if err != nil { t.Fatal(err) } - if err = db.View(func(tx *bolt.Tx) error { return <-tx.Check(bolt.HexKVStringer()) }); err != nil { + if err = db.View(func(tx *bolt.Tx) error { return <-tx.Check() }); err != nil { t.Fatal(err) } if err = db.Close(); err != nil { @@ -409,7 +409,7 @@ func TestOpen_Check(t *testing.T) { if err != nil { t.Fatal(err) } - if err := db.View(func(tx *bolt.Tx) error { return <-tx.Check(bolt.HexKVStringer()) }); err != nil { + if err := db.View(func(tx *bolt.Tx) error { return <-tx.Check() }); err != nil { t.Fatal(err) } if err := db.Close(); err != nil { diff --git a/internal/btesting/btesting.go b/internal/btesting/btesting.go index 1dc05a8ef..b30507234 100644 --- a/internal/btesting/btesting.go +++ b/internal/btesting/btesting.go @@ -119,7 +119,7 @@ func (db *DB) MustCheck() { err := db.Update(func(tx *bolt.Tx) error { // Collect all the errors. var errors []error - for err := range tx.Check(bolt.HexKVStringer()) { + for err := range tx.Check() { errors = append(errors, err) if len(errors) > 10 { break diff --git a/internal/tests/tx_check_test.go b/internal/tests/tx_check_test.go index 8d67db307..0476d010f 100644 --- a/internal/tests/tx_check_test.go +++ b/internal/tests/tx_check_test.go @@ -39,7 +39,7 @@ func TestTx_RecursivelyCheckPages_MisplacedPage(t *testing.T) { require.NoError(t, db.Update(func(tx *bolt.Tx) error { // Collect all the errors. var errors []error - for err := range tx.Check(bolt.HexKVStringer()) { + for err := range tx.Check() { errors = append(errors, err) } require.Len(t, errors, 1) @@ -75,7 +75,7 @@ func TestTx_RecursivelyCheckPages_CorruptedLeaf(t *testing.T) { require.NoError(t, db.Update(func(tx *bolt.Tx) error { // Collect all the errors. var errors []error - for err := range tx.Check(bolt.HexKVStringer()) { + for err := range tx.Check() { errors = append(errors, err) } require.Len(t, errors, 2) diff --git a/tx.go b/tx.go index 29790b7e8..2fac8c0a7 100644 --- a/tx.go +++ b/tx.go @@ -200,7 +200,7 @@ func (tx *Tx) Commit() error { // If strict mode is enabled then perform a consistency check. if tx.db.StrictMode { - ch := tx.Check(HexKVStringer()) + ch := tx.Check() var errs []string for { err, ok := <-ch diff --git a/tx_check.go b/tx_check.go index 23e22c3da..75c7c0843 100644 --- a/tx_check.go +++ b/tx_check.go @@ -13,9 +13,22 @@ import ( // because of caching. This overhead can be removed if running on a read-only // transaction, however, it is not safe to execute other writer transactions at // the same time. -func (tx *Tx) Check(kvStringer KVStringer) <-chan error { +func (tx *Tx) Check() <-chan error { + return tx.CheckWithOptions() +} + +// CheckWithOptions allows users to provide a customized `KVStringer` implementation, +// so that bolt can generate human-readable diagnostic messages. +func (tx *Tx) CheckWithOptions(options ...CheckOption) <-chan error { + chkConfig := checkConfig{ + kvStringer: HexKVStringer(), + } + for _, op := range options { + op(&chkConfig) + } + ch := make(chan error) - go tx.check(kvStringer, ch) + go tx.check(chkConfig.kvStringer, ch) return ch } @@ -179,6 +192,18 @@ func verifyKeyOrder(pgId pgid, pageType string, index int, key []byte, previousK // =========================================================================================== +type checkConfig struct { + kvStringer KVStringer +} + +type CheckOption func(options *checkConfig) + +func WithKVStringer(kvStringer KVStringer) CheckOption { + return func(c *checkConfig) { + c.kvStringer = kvStringer + } +} + // KVStringer allows to prepare human-readable diagnostic messages. type KVStringer interface { KeyToString([]byte) string diff --git a/tx_test.go b/tx_test.go index d972def3f..fa8302d58 100644 --- a/tx_test.go +++ b/tx_test.go @@ -50,7 +50,7 @@ func TestTx_Check_ReadOnly(t *testing.T) { numChecks := 2 errc := make(chan error, numChecks) check := func() { - errc <- <-tx.Check(bolt.HexKVStringer()) + errc <- <-tx.Check() } // Ensure the freelist is not reloaded and does not race. for i := 0; i < numChecks; i++ {