Skip to content

Commit 9e13b22

Browse files
committed
add shrink/save/drop command
1 parent 2b54c6f commit 9e13b22

8 files changed

+239
-49
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ go get -u -v github.com/Sora233/buntdb-cli
3737
* show
3838
* keys
3939
* use
40+
* shrink
41+
* save
4042

4143
You can provide -h flag for command to print help message.
4244
![get](https://user-images.githubusercontent.com/11474360/104104364-81e09e00-52e2-11eb-8863-391420bf6064.jpg)
4345

4446
### TODO
4547

46-
- [ ] create index
48+
- [ ] create index (Index is memory-only, You need to create index everytime you restart, so I am considering whether to
49+
impl it)

cli/completer.go

+53-9
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ func BuntdbCompleter(d prompt.Document) []prompt.Suggest {
1414
args := strings.Split(d.TextBeforeCursor(), " ")
1515
if len(args) == 1 {
1616
// input command
17-
return cmdCompleter(args[0])
17+
return cmdCompleter(d, args[0])
1818
} else {
19-
return optionCompleter(args[0], args[1:])
19+
return optionCompleter(d, args[0], args[1:])
2020
}
2121
}
2222

23-
func cmdCompleter(cmd string) []prompt.Suggest {
23+
func cmdCompleter(d prompt.Document, cmd string) []prompt.Suggest {
2424
if Debug {
25-
fmt.Printf("cmdCompleter %v\n", cmd)
25+
fmt.Printf("|cmdCompleter %v|\n", cmd)
2626
}
2727
cmds := []prompt.Suggest{
2828
{Text: "get", Description: "get command"},
@@ -33,12 +33,15 @@ func cmdCompleter(cmd string) []prompt.Suggest {
3333
{Text: "keys", Description: "iterate keys"},
3434
{Text: "use", Description: "change db"},
3535
{Text: "exit", Description: "exit buntdb shell client"},
36+
{Text: "drop", Description: "drop the index"},
3637
}
3738
tx, _ := db.GetCurrentTransaction()
3839
if tx == nil {
3940
cmds = append(cmds,
4041
prompt.Suggest{Text: "rbegin", Description: "open a readonly transaction"},
4142
prompt.Suggest{Text: "rwbegin", Description: "open a read/write transaction"},
43+
prompt.Suggest{Text: "shrink", Description: "shrink command"},
44+
prompt.Suggest{Text: "save", Description: "save db to file"},
4245
)
4346
} else {
4447
cmds = append(cmds,
@@ -49,28 +52,69 @@ func cmdCompleter(cmd string) []prompt.Suggest {
4952
return prompt.FilterHasPrefix(cmds, cmd, true)
5053
}
5154

52-
func optionCompleter(cmd string, args []string) []prompt.Suggest {
55+
func optionCompleter(d prompt.Document, cmd string, args []string) []prompt.Suggest {
5356
if Debug {
54-
fmt.Printf("optionCompleter %v %v\n", cmd, args)
57+
fmt.Printf("|optionCompleter %v [%v]|\n", cmd, strings.Join(args, ":"))
5558
}
59+
var result = make([]prompt.Suggest, 0)
5660
switch cmd {
5761
case "get":
5862
case "set":
5963
case "del":
6064
case "ttl":
6165
case "show":
62-
return []prompt.Suggest{
66+
result = []prompt.Suggest{
6367
{Text: "index"},
6468
{Text: "db"},
6569
}
70+
if len(args) == 0 {
71+
break
72+
}
73+
arg := args[0]
74+
if Debug {
75+
fmt.Printf("|arg %v|", arg)
76+
}
77+
switch arg {
78+
case "index":
79+
result = []prompt.Suggest{}
80+
case "db":
81+
result = []prompt.Suggest{}
82+
default:
83+
result = prompt.FilterHasPrefix(result, arg, true)
84+
}
6685
case "keys":
6786
case "use":
6887
case "rbegin":
6988
case "rwbegin":
7089
case "rollback":
7190
case "commit":
91+
case "shrink":
92+
case "save":
93+
case "drop":
94+
result = []prompt.Suggest{
95+
{Text: "index"},
96+
}
97+
if len(args) == 0 {
98+
break
99+
}
100+
switch args[0] {
101+
case "index":
102+
result = []prompt.Suggest{}
103+
tx, _, closeTx := db.GetCurrentOrNewTransaction()
104+
defer closeTx()
105+
indexes, err := tx.Indexes()
106+
if err == nil {
107+
for _, index := range indexes {
108+
result = append(result, prompt.Suggest{Text: index})
109+
}
110+
if len(args) >= 2 {
111+
result = prompt.FilterHasPrefix(result, args[1], true)
112+
}
113+
}
114+
default:
115+
result = prompt.FilterHasPrefix(result, args[0], true)
116+
}
72117
default:
73-
return []prompt.Suggest{}
74118
}
75-
return []prompt.Suggest{}
119+
return result
76120
}

cli/completer_test.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ func TestBuntdbCompleter(t *testing.T) {
2626
d = buf.Document()
2727
assert.NotNil(t, d)
2828
sug = BuntdbCompleter(*d)
29-
assert.Len(t, sug, 2)
30-
text := []string{sug[0].Text, sug[1].Text}
29+
var text []string
30+
for _, s := range sug {
31+
text = append(text, s.Text)
32+
}
3133
assert.Contains(t, text, "set")
3234
assert.Contains(t, text, "show")
35+
assert.Contains(t, text, "shrink")
36+
assert.Contains(t, text, "save")
3337

3438
buf.DeleteBeforeCursor(999)
3539
buf.InsertText("get ", false, true)
@@ -58,6 +62,7 @@ func TestBuntdbCompleter(t *testing.T) {
5862
buf.DeleteBeforeCursor(999)
5963

6064
db.InitBuntDB(":memory:")
65+
defer db.Close()
6166
db.Begin(true)
6267
buf.InsertText("r", false, true)
6368
d = buf.Document()
@@ -66,6 +71,24 @@ func TestBuntdbCompleter(t *testing.T) {
6671
assert.Len(t, sug, 1)
6772
assert.Equal(t, "rollback", sug[0].Text)
6873
db.Rollback()
69-
db.Close()
7074

75+
buf.DeleteBeforeCursor(999)
76+
buf.InsertText("dr", false, true)
77+
d = buf.Document()
78+
assert.NotNil(t, d)
79+
sug = BuntdbCompleter(*d)
80+
assert.Len(t, sug, 1)
81+
assert.Equal(t, "drop", sug[0].Text)
82+
83+
buf.InsertText("op ", false, true)
84+
d = buf.Document()
85+
assert.NotNil(t, d)
86+
sug = BuntdbCompleter(*d)
87+
assert.Len(t, sug, 1)
88+
assert.Equal(t, "index", sug[0].Text)
89+
buf.InsertText("index", false, true)
90+
d = buf.Document()
91+
assert.NotNil(t, d)
92+
sug = BuntdbCompleter(*d)
93+
assert.Len(t, sug, 0)
7194
}

cli/executor.go

+36-29
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@ import (
77
"strings"
88
)
99

10+
type transactionRequireType int64
11+
12+
const (
13+
noNeed transactionRequireType = iota
14+
any
15+
nonNil
16+
)
17+
18+
func commandTransactionRequireType(command string) transactionRequireType {
19+
switch command {
20+
case "rwbegin", "rbegin", "rollback", "commit", "shrink", "save":
21+
return noNeed
22+
case "use":
23+
return any
24+
default:
25+
return nonNil
26+
}
27+
}
28+
1029
func BuntdbExecutor(s string) {
1130
s = strings.TrimSpace(s)
1231
if s == "" || s == "exit" {
@@ -32,52 +51,40 @@ func BuntdbExecutor(s string) {
3251
return
3352
}
3453
cmd := ctx.Selected().Name
35-
if cmd == "rwbegin" || cmd == "rbegin" || cmd == "rollback" || cmd == "commit" {
54+
switch commandTransactionRequireType(cmd) {
55+
case noNeed:
3656
err = ctx.Run()
3757
if err != nil {
3858
fmt.Printf("ERR: %v\n", err)
3959
}
4060
return
41-
}
42-
tx, rw := db.GetCurrentTransaction()
43-
if ctx.Selected().Name == "use" {
44-
err = ctx.Run(tx)
45-
if err != nil {
46-
fmt.Printf("ERR: %v\n", err)
47-
}
48-
return
49-
}
50-
if tx != nil {
51-
if Debug {
52-
fmt.Printf("got current %v transaction\n", db.RWDescribe(rw))
53-
}
54-
err = ctx.Run(tx)
55-
if err != nil {
56-
fmt.Printf("ERR: %v\n", err)
61+
case any:
62+
tx, _ := db.GetCurrentTransaction()
63+
if cmd == "use" {
64+
err = ctx.Run(tx)
65+
if err != nil {
66+
fmt.Printf("ERR: %v\n", err)
67+
}
5768
return
5869
}
59-
} else {
70+
case nonNil:
71+
tx, rw, closeTx := db.GetCurrentOrNewTransaction()
6072
if Debug {
61-
fmt.Printf("no transaction, create a rw transaction\n")
62-
}
63-
tx, err := db.Begin(true)
64-
if err != nil {
65-
fmt.Printf("ERR: %v\n", err)
66-
return
73+
fmt.Printf("GetCurrentOrNewTransaction(%v)\n", db.RWDescribe(rw))
6774
}
6875
defer func() {
69-
if Debug {
70-
fmt.Printf("transaction commit\n")
71-
}
72-
err := db.Commit()
76+
err = closeTx()
7377
if err != nil {
74-
fmt.Printf("ERR: commit error %v\n", err)
78+
fmt.Printf("ERR: %v\n", err)
7579
}
7680
}()
7781
err = ctx.Run(tx)
7882
if err != nil {
7983
fmt.Printf("ERR: %v\n", err)
8084
return
8185
}
86+
default:
87+
fmt.Printf("ERR: unknown transaction require\n")
88+
return
8289
}
8390
}

cli/executor_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ func TestBuntdbExecutor(t *testing.T) {
9797
BuntdbExecutor("rbegin")
9898
BuntdbExecutor("del x")
9999
BuntdbExecutor("del y")
100+
BuntdbExecutor("shrink")
101+
BuntdbExecutor("save testcli_save")
102+
_, err = os.Lstat("testcli_save")
103+
assert.Nil(t, err)
100104
BuntdbExecutor("commit")
101105
BuntdbExecutor("rollback")
102106
bd.View(func(tx *buntdb.Tx) error {
@@ -108,15 +112,24 @@ func TestBuntdbExecutor(t *testing.T) {
108112
assert.Equal(t, "x", val)
109113
return nil
110114
})
115+
BuntdbExecutor("shrink")
116+
BuntdbExecutor("set a xy")
117+
BuntdbExecutor("save testcli_save")
118+
_, err = os.Lstat("testcli_save")
119+
assert.Nil(t, err)
120+
BuntdbExecutor("save testcli_save")
121+
BuntdbExecutor("save --force testcli_save")
111122

112123
BuntdbExecutor("use testcli-2")
113124
BuntdbExecutor("use -c testcli-2")
114125
assert.Equal(t, db.GetDbPath(), "testcli-2")
115126
BuntdbExecutor("use -c testcli")
116127
assert.Equal(t, db.GetDbPath(), "testcli")
128+
BuntdbExecutor("use :memory:")
117129
BuntdbExecutor("exit")
118130
BuntdbExecutor("")
119131

132+
os.Remove("testcli_save")
120133
os.Remove("testcli")
121134
os.Remove("testcli-2")
122135
}

cli/grammar.go

+47-7
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,11 @@ func (u *UseGrammar) Run(ctx *kong.Context, tx *buntdb.Tx) error {
123123
if u.Create {
124124
return db.InitBuntDB(u.Path)
125125
} else {
126-
fmt.Fprintf(ctx.Stdout, "%v does not exist, set --create to create it.\n", u.Path)
127-
return nil
126+
return fmt.Errorf("%v does not exist, set --create to create it", u.Path)
128127
}
129128
}
130129
if f.IsDir() {
131-
fmt.Fprintf(ctx.Stdout, "%v is a dir.\n", u.Path)
132-
return nil
130+
return fmt.Errorf("%v is a dir", u.Path)
133131
}
134132
return db.InitBuntDB(u.Path)
135133
}
@@ -146,10 +144,9 @@ func (t *TTLGrammar) Run(ctx *kong.Context, tx *buntdb.Tx) error {
146144
return nil
147145
}
148146
return err
149-
} else {
150-
fmt.Fprintln(ctx.Stdout, int64(ttl.Seconds()))
151-
return nil
152147
}
148+
fmt.Fprintln(ctx.Stdout, int64(ttl.Seconds()))
149+
return nil
153150
}
154151

155152
type RWBeginGrammar struct{}
@@ -178,6 +175,46 @@ func (r *RollbackGrammar) Run(ctx *kong.Context) error {
178175
return db.Rollback()
179176
}
180177

178+
type ShrinkGrammar struct{}
179+
180+
func (s *ShrinkGrammar) Run(ctx *kong.Context) error {
181+
return db.Shrink()
182+
}
183+
184+
type SaveGrammar struct {
185+
Path string `arg:"" help:"the path to save"`
186+
Force bool `optional:"" help:"overwrite if the path exists"`
187+
}
188+
189+
func (s *SaveGrammar) Run(ctx *kong.Context) error {
190+
f, err := os.Lstat(s.Path)
191+
if err == nil {
192+
if f.IsDir() {
193+
return fmt.Errorf("%v is a dir", s.Path)
194+
}
195+
if !s.Force {
196+
return fmt.Errorf("%v exist, use --force to overwrite it", s.Path)
197+
}
198+
}
199+
file, err := os.Create(s.Path)
200+
if err != nil {
201+
return err
202+
}
203+
return db.Save(file)
204+
}
205+
206+
type DropGrammar struct {
207+
Index DropIndexGrammar `cmd:"" help:"drop the index with the given name"`
208+
}
209+
210+
type DropIndexGrammar struct {
211+
Name string `arg:"" help:"the index name to drop"`
212+
}
213+
214+
func (s *DropIndexGrammar) Run(ctx *kong.Context, tx *buntdb.Tx) error {
215+
return tx.DropIndex(s.Name)
216+
}
217+
181218
type Grammar struct {
182219
Get GetGrammar `cmd:"" help:"get a value from key, return the value if key exists, or <nil> if non-exists."`
183220
Set SetGrammar `cmd:"" help:"set a key-value [ttl], return the old value, or <nil> if old value doesn't exist."`
@@ -190,6 +227,9 @@ type Grammar struct {
190227
RBegin RBeginGrammar `cmd:"" name:"rbegin" help:"begin a readonly transaction"`
191228
Commit CommitGrammar `cmd:"" help:"commit a transaction"`
192229
Rollback RollbackGrammar `cmd:"" help:"rollback a transaction"`
230+
Shrink ShrinkGrammar `cmd:"" help:"run database shrink command"`
231+
Save SaveGrammar `cmd:"" help:"save the db to file"`
232+
Drop DropGrammar `cmd:"" help:"drop command"`
193233
Exit bool `kong:"-"`
194234
}
195235

0 commit comments

Comments
 (0)