Skip to content

Commit

Permalink
Fix bolt CLI tool print entire freelist, and to dump keys/value bytes…
Browse files Browse the repository at this point in the history
… of leaf elements.
  • Loading branch information
jpbetz committed Sep 14, 2017
1 parent a4199f8 commit fefb4a0
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 9 deletions.
Binary file added cmd/bolt/bolt
Binary file not shown.
86 changes: 77 additions & 9 deletions cmd/bolt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,18 +438,31 @@ func newPageCommand(m *Main) *PageCommand {
}
}

type pageOptions struct {
help bool
itemKey int
itemValue int
}

// Run executes the command.
func (cmd *PageCommand) Run(args ...string) error {
// Parse flags.
options := &pageOptions{}
fs := flag.NewFlagSet("", flag.ContinueOnError)
help := fs.Bool("h", false, "")
fs.IntVar(&options.itemKey, "dump-item-key", -1, "Prints out the raw bytes of the leaf item key at the given index.")
fs.IntVar(&options.itemValue, "dump-item-value", -1, "Prints out the raw bytes of the leaf item value at the given index.")
fs.BoolVar(&options.help, "h", false, "")
if err := fs.Parse(args); err != nil {
return err
} else if *help {
} else if options.help {
fmt.Fprintln(cmd.Stderr, cmd.Usage())
return ErrUsage
}

if options.itemKey > -1 && options.itemValue > -1 {
return fmt.Errorf("--dump-item-key or --dump-item-value may be provided, but not both.")
}

// Require database path and page id.
path := fs.Arg(0)
if path == "" {
Expand Down Expand Up @@ -487,16 +500,24 @@ func (cmd *PageCommand) Run(args ...string) error {
}

// Print basic page info.
fmt.Fprintf(cmd.Stdout, "Page ID: %d\n", p.id)
fmt.Fprintf(cmd.Stdout, "Page Type: %s\n", p.Type())
fmt.Fprintf(cmd.Stdout, "Total Size: %d bytes\n", len(buf))
if options.itemKey == -1 && options.itemValue == -1 {
fmt.Fprintf(cmd.Stdout, "Page ID: %d\n", p.id)
fmt.Fprintf(cmd.Stdout, "Page Type: %s\n", p.Type())
fmt.Fprintf(cmd.Stdout, "Total Size: %d bytes\n", len(buf))
}

// Print type-specific data.
switch p.Type() {
case "meta":
err = cmd.PrintMeta(cmd.Stdout, buf)
case "leaf":
err = cmd.PrintLeaf(cmd.Stdout, buf)
if options.itemKey > -1 {
err = cmd.PrintLeafItemKey(cmd.Stdout, buf, uint16(options.itemKey))
} else if options.itemValue > -1 {
err = cmd.PrintLeafItemValue(cmd.Stdout, buf, uint16(options.itemValue))
} else {
err = cmd.PrintLeaf(cmd.Stdout, buf)
}
case "branch":
err = cmd.PrintBranch(cmd.Stdout, buf)
case "freelist":
Expand Down Expand Up @@ -562,6 +583,37 @@ func (cmd *PageCommand) PrintLeaf(w io.Writer, buf []byte) error {
return nil
}

// leafPageElement retrieves a leaf page element.
func (cmd *PageCommand) leafPageElement(pageBytes []byte, index uint16) (*leafPageElement, error) {
p := (*page)(unsafe.Pointer(&pageBytes[0]))

if index >= p.count {
return nil, fmt.Errorf("Expected item index >= 0 and < %d, but got %d.", p.count, index)
}

return p.leafPageElement(index), nil
}

// PrintLeafItemKey writes the bytes of a leaf element's key.
func (cmd *PageCommand) PrintLeafItemKey(w io.Writer, pageBytes []byte, index uint16) error {
e, err := cmd.leafPageElement(pageBytes, index)
if err != nil {
return err
}
_, err = w.Write(e.key())
return err
}

// PrintLeafItemKey writes the bytes of a leaf element's value.
func (cmd *PageCommand) PrintLeafItemValue(w io.Writer, pageBytes []byte, index uint16) error {
e, err := cmd.leafPageElement(pageBytes, index)
if err != nil {
return err
}
_, err = w.Write(e.value())
return err
}

// PrintBranch prints the data for a leaf page.
func (cmd *PageCommand) PrintBranch(w io.Writer, buf []byte) error {
p := (*page)(unsafe.Pointer(&buf[0]))
Expand Down Expand Up @@ -593,12 +645,21 @@ func (cmd *PageCommand) PrintFreelist(w io.Writer, buf []byte) error {
p := (*page)(unsafe.Pointer(&buf[0]))

// Print number of items.
fmt.Fprintf(w, "Item Count: %d\n", p.count)
fmt.Fprintf(w, "Item Count Field: %d\n", p.count)
fmt.Fprintf(w, "Overflow: %d\n", p.overflow)

idx, count := 0, int(p.count)
if p.count == 0xFFFF {
idx = 1
count = int(((*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr)))[0])
fmt.Fprintf(w, "Item Count of 65535 (max uint16) indicates a freelist overflow. Actual Item Count: %d\n", count)
}

fmt.Fprintf(w, "\n")

// Print each page in the freelist.
ids := (*[maxAllocSize]pgid)(unsafe.Pointer(&p.ptr))
for i := uint16(0); i < p.count; i++ {
for i := int(idx); i < count; i++ {
fmt.Fprintf(w, "%d\n", ids[i])
}
fmt.Fprintf(w, "\n")
Expand Down Expand Up @@ -653,7 +714,14 @@ func (cmd *PageCommand) PrintPage(w io.Writer, r io.ReaderAt, pageID int, pageSi
// Usage returns the help message.
func (cmd *PageCommand) Usage() string {
return strings.TrimLeft(`
usage: bolt page -page PATH pageid [pageid...]
usage: bolt page PATH pageid [pageid...]
Additional options include:
--dump-item-key NUM
Prints out the raw bytes of the leaf item key at the given index.
--dump-item-value NUM
Prints out the raw bytes of the leaf item value at the given index.
Page prints one or more pages in human readable format.
`, "\n")
Expand Down

0 comments on commit fefb4a0

Please sign in to comment.