mirror of
https://github.com/etcd-io/bbolt.git
synced 2025-05-31 11:42:30 +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
|
// ErrNonDivisibleBatchSize is returned when the batch size can't be evenly
|
||||||
// divided by the iteration count.
|
// divided by the iteration count.
|
||||||
ErrNonDivisibleBatchSize = errors.New("number of iterations must be divisible by the batch size")
|
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() {
|
func main() {
|
||||||
@ -87,6 +90,8 @@ func (m *Main) Run(args ...string) error {
|
|||||||
return newBenchCommand(m).Run(args[1:]...)
|
return newBenchCommand(m).Run(args[1:]...)
|
||||||
case "check":
|
case "check":
|
||||||
return newCheckCommand(m).Run(args[1:]...)
|
return newCheckCommand(m).Run(args[1:]...)
|
||||||
|
case "dump":
|
||||||
|
return newDumpCommand(m).Run(args[1:]...)
|
||||||
case "info":
|
case "info":
|
||||||
return newInfoCommand(m).Run(args[1:]...)
|
return newInfoCommand(m).Run(args[1:]...)
|
||||||
case "pages":
|
case "pages":
|
||||||
@ -264,6 +269,118 @@ Info prints basic information about the Bolt database at PATH.
|
|||||||
`, "\n")
|
`, "\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.
|
// PagesCommand represents the "pages" command execution.
|
||||||
type PagesCommand struct {
|
type PagesCommand struct {
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
|
Loading…
x
Reference in New Issue
Block a user