Skip to content

Commit

Permalink
Merge pull request #484 from ahrtr/surgery_20230507
Browse files Browse the repository at this point in the history
cmd: migrate 'surgery clear-page' command to cobra style command
  • Loading branch information
ahrtr authored May 16, 2023
2 parents fbc136d + 8974e91 commit bd6b79c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func newSurgeryCobraCommand() *cobra.Command {

surgeryCmd.AddCommand(newSurgeryRevertMetaPageCommand())
surgeryCmd.AddCommand(newSurgeryCopyPageCommand())
surgeryCmd.AddCommand(newSurgeryClearPageCommand())
surgeryCmd.AddCommand(newSurgeryClearPageElementsCommand())
surgeryCmd.AddCommand(newSurgeryFreelistCommand())

Expand Down Expand Up @@ -138,6 +139,10 @@ func newSurgeryCopyPageCommand() *cobra.Command {
}

func surgeryCopyPageFunc(srcDBPath string, cfg surgeryCopyPageOptions) error {
if _, err := checkSourceDBPath(srcDBPath); err != nil {
return err
}

if err := common.CopyFile(srcDBPath, cfg.outputDBFilePath); err != nil {
return fmt.Errorf("[copy-page] copy file failed: %w", err)
}
Expand All @@ -159,6 +164,74 @@ func surgeryCopyPageFunc(srcDBPath string, cfg surgeryCopyPageOptions) error {
return nil
}

type surgeryClearPageOptions struct {
surgeryBaseOptions
pageId uint64
}

func (o *surgeryClearPageOptions) AddFlags(fs *pflag.FlagSet) {
o.surgeryBaseOptions.AddFlags(fs)
fs.Uint64VarP(&o.pageId, "pageId", "", o.pageId, "page Id")
}

func (o *surgeryClearPageOptions) Validate() error {
if err := o.surgeryBaseOptions.Validate(); err != nil {
return err
}
if o.pageId < 2 {
return fmt.Errorf("the pageId must be at least 2, but got %d", o.pageId)
}
return nil
}

func newSurgeryClearPageCommand() *cobra.Command {
var o surgeryClearPageOptions
clearPageCmd := &cobra.Command{
Use: "clear-page <bbolt-file> [options]",
Short: "Clears all elements from the given page, which can be a branch or leaf page",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return errors.New("db file path not provided")
}
if len(args) > 1 {
return errors.New("too many arguments")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Validate(); err != nil {
return err
}
return surgeryClearPageFunc(args[0], o)
},
}
o.AddFlags(clearPageCmd.Flags())
return clearPageCmd
}

func surgeryClearPageFunc(srcDBPath string, cfg surgeryClearPageOptions) error {
if _, err := checkSourceDBPath(srcDBPath); err != nil {
return err
}

if err := common.CopyFile(srcDBPath, cfg.outputDBFilePath); err != nil {
return fmt.Errorf("[clear-page] copy file failed: %w", err)
}

needAbandonFreelist, err := surgeon.ClearPage(cfg.outputDBFilePath, common.Pgid(cfg.pageId))
if err != nil {
return fmt.Errorf("clear-page command failed: %w", err)
}

if needAbandonFreelist {
fmt.Fprintf(os.Stdout, "WARNING: The clearing has abandoned some pages that are not yet referenced from free list.\n")
fmt.Fprintf(os.Stdout, "Please consider executing `./bbolt surgery abandon-freelist ...`\n")
}

fmt.Fprintf(os.Stdout, "The page (%d) was cleared\n", cfg.pageId)
return nil
}

type surgeryClearPageElementsOptions struct {
surgeryBaseOptions
pageId uint64
Expand Down Expand Up @@ -209,6 +282,10 @@ func newSurgeryClearPageElementsCommand() *cobra.Command {
}

func surgeryClearPageElementFunc(srcDBPath string, cfg surgeryClearPageElementsOptions) error {
if _, err := checkSourceDBPath(srcDBPath); err != nil {
return err
}

if err := common.CopyFile(srcDBPath, cfg.outputDBFilePath); err != nil {
return fmt.Errorf("[clear-page-element] copy file failed: %w", err)
}
Expand All @@ -227,9 +304,6 @@ func surgeryClearPageElementFunc(srcDBPath string, cfg surgeryClearPageElementsO
return nil
}

// TODO(ahrtr): add `bbolt surgery freelist rebuild/check ...` commands,
// and move all `surgery freelist` commands into a separate file,
// e.g command_surgery_freelist.go.
func newSurgeryFreelistCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "freelist <subcommand>",
Expand Down Expand Up @@ -269,6 +343,10 @@ func newSurgeryFreelistAbandonCommand() *cobra.Command {
}

func surgeryFreelistAbandonFunc(srcDBPath string, cfg surgeryBaseOptions) error {
if _, err := checkSourceDBPath(srcDBPath); err != nil {
return err
}

if err := common.CopyFile(srcDBPath, cfg.outputDBFilePath); err != nil {
return fmt.Errorf("[freelist abandon] copy file failed: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,43 @@ func TestSurgery_CopyPage(t *testing.T) {
assert.Equal(t, pageDataWithoutPageId(srcPageId3Data), pageDataWithoutPageId(dstPageId2Data))
}

// TODO(ahrtr): add test case below for `surgery clear-page` command:
// 1. The page is a branch page. All its children should become free pages.
func TestSurgery_ClearPage(t *testing.T) {
pageSize := 4096
db := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: pageSize})
srcPath := db.Path()

// Insert some sample data
t.Log("Insert some sample data")
err := db.Fill([]byte("data"), 1, 20,
func(tx int, k int) []byte { return []byte(fmt.Sprintf("%04d", k)) },
func(tx int, k int) []byte { return make([]byte, 10) },
)
require.NoError(t, err)

defer requireDBNoChange(t, dbData(t, srcPath), srcPath)

// clear page 3
t.Log("clear page 3")
rootCmd := main.NewRootCommand()
output := filepath.Join(t.TempDir(), "dstdb")
rootCmd.SetArgs([]string{
"surgery", "clear-page", srcPath,
"--output", output,
"--pageId", "3",
})
err = rootCmd.Execute()
require.NoError(t, err)

t.Log("Verify result")
dstPageId3Data := readPage(t, output, 3, pageSize)

p := common.LoadPage(dstPageId3Data)
assert.Equal(t, uint16(0), p.Count())
assert.Equal(t, uint32(0), p.Overflow())
}

func TestSurgery_ClearPageElements_Without_Overflow(t *testing.T) {
testCases := []struct {
name string
Expand Down
2 changes: 0 additions & 2 deletions cmd/bbolt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ func (m *Main) Run(args ...string) error {
return newPagesCommand(m).Run(args[1:]...)
case "stats":
return newStatsCommand(m).Run(args[1:]...)
case "surgery":
return newSurgeryCommand(m).Run(args[1:]...)
default:
return ErrUnknownCommand
}
Expand Down
146 changes: 0 additions & 146 deletions cmd/bbolt/surgery_commands.go

This file was deleted.

47 changes: 0 additions & 47 deletions cmd/bbolt/surgery_commands_test.go

This file was deleted.

0 comments on commit bd6b79c

Please sign in to comment.