mirror of
https://github.com/etcd-io/bbolt.git
synced 2025-05-30 11:12:08 +00:00
Add 'bolt dump' command.
This commit is contained in:
parent
dd542876fa
commit
3ad30436da
117
cmd/bolt/main.go
117
cmd/bolt/main.go
@ -42,6 +42,9 @@ var (
|
||||
// ErrNonDivisibleBatchSize is returned when the batch size can't be evenly
|
||||
// divided by the iteration count.
|
||||
ErrNonDivisibleBatchSize = errors.New("number of iterations must be divisible by the batch size")
|
||||
|
||||
// ErrPageIDRequired is returned when a required page id is not specified.
|
||||
ErrPageIDRequired = errors.New("page id required")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -87,6 +90,8 @@ func (m *Main) Run(args ...string) error {
|
||||
return newBenchCommand(m).Run(args[1:]...)
|
||||
case "check":
|
||||
return newCheckCommand(m).Run(args[1:]...)
|
||||
case "dump":
|
||||
return newDumpCommand(m).Run(args[1:]...)
|
||||
case "info":
|
||||
return newInfoCommand(m).Run(args[1:]...)
|
||||
case "pages":
|
||||
@ -264,6 +269,118 @@ Info prints basic information about the Bolt database at PATH.
|
||||
`, "\n")
|
||||
}
|
||||
|
||||
// DumpCommand represents the "dump" command execution.
|
||||
type DumpCommand struct {
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
}
|
||||
|
||||
// newDumpCommand returns a DumpCommand.
|
||||
func newDumpCommand(m *Main) *DumpCommand {
|
||||
return &DumpCommand{
|
||||
Stdin: m.Stdin,
|
||||
Stdout: m.Stdout,
|
||||
Stderr: m.Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the command.
|
||||
func (cmd *DumpCommand) Run(args ...string) error {
|
||||
// Parse flags.
|
||||
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
help := fs.Bool("h", false, "")
|
||||
pageID := fs.Int("page", -1, "")
|
||||
if err := fs.Parse(args); err != nil {
|
||||
return err
|
||||
} else if *help {
|
||||
fmt.Fprintln(cmd.Stderr, cmd.Usage())
|
||||
return ErrUsage
|
||||
}
|
||||
|
||||
// Require database path and page id.
|
||||
path := fs.Arg(0)
|
||||
if path == "" {
|
||||
return ErrPathRequired
|
||||
} else if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return ErrFileNotFound
|
||||
} else if *pageID == -1 {
|
||||
return ErrPageIDRequired
|
||||
}
|
||||
|
||||
// Open database to retrieve page size.
|
||||
db, err := bolt.Open(path, 0666, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pageSize := db.Info().PageSize
|
||||
_ = db.Close()
|
||||
|
||||
// Open database file handler.
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
// Print page to stdout.
|
||||
return cmd.PrintPage(cmd.Stdout, f, *pageID, pageSize)
|
||||
}
|
||||
|
||||
// PrintPage prints a given page as hexidecimal.
|
||||
func (cmd *DumpCommand) PrintPage(w io.Writer, r io.ReaderAt, pageID int, pageSize int) error {
|
||||
const bytesPerLineN = 16
|
||||
|
||||
// Read page into buffer.
|
||||
buf := make([]byte, pageSize)
|
||||
addr := pageID * pageSize
|
||||
if n, err := r.ReadAt(buf, int64(addr)); err != nil {
|
||||
return err
|
||||
} else if n != pageSize {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
// Write out to writer in 16-byte lines.
|
||||
var prev []byte
|
||||
var skipped bool
|
||||
for offset := 0; offset < pageSize; offset += bytesPerLineN {
|
||||
// Retrieve current 16-byte line.
|
||||
line := buf[offset : offset+bytesPerLineN]
|
||||
isLastLine := (offset == (pageSize - bytesPerLineN))
|
||||
|
||||
// If it's the same as the previous line then print a skip.
|
||||
if bytes.Equal(line, prev) && !isLastLine {
|
||||
if !skipped {
|
||||
fmt.Fprintf(w, "%07x *\n", addr+offset)
|
||||
skipped = true
|
||||
}
|
||||
} else {
|
||||
// Print line as hexadecimal in 2-byte groups.
|
||||
fmt.Fprintf(w, "%07x %04x %04x %04x %04x %04x %04x %04x %04x\n", addr+offset,
|
||||
line[0:2], line[2:4], line[4:6], line[6:8],
|
||||
line[8:10], line[10:12], line[12:14], line[14:16],
|
||||
)
|
||||
|
||||
skipped = false
|
||||
}
|
||||
|
||||
// Save the previous line.
|
||||
prev = line
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Usage returns the help message.
|
||||
func (cmd *DumpCommand) Usage() string {
|
||||
return strings.TrimLeft(`
|
||||
usage: bolt dump -page PAGEID PATH
|
||||
|
||||
Dump prints a hexidecimal dump of a single page.
|
||||
`, "\n")
|
||||
}
|
||||
|
||||
// PagesCommand represents the "pages" command execution.
|
||||
type PagesCommand struct {
|
||||
Stdin io.Reader
|
||||
|
Loading…
x
Reference in New Issue
Block a user