Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 58 additions & 23 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
plog "github.com/pingcap/log"
)

const Json_separator = "Xuan123#@!xuan"

var (
pdAddr = flag.String("pd", "localhost:2379", "PD addr")
clientLog = flag.String("log-file", "/dev/null", "TiKV client log file")
Expand All @@ -37,11 +39,13 @@ var RegisteredCmds = []tcli.Cmd{
kvcmds.ScanPrefixCmd{},
kvcmds.HeadCmd{},
kvcmds.PutCmd{},
kvcmds.PutJsonCmd{},
kvcmds.BackupCmd{},
kvcmds.NewBenchCmd(
kvcmds.NewYcsbBench(*pdAddr),
),
kvcmds.GetCmd{},
kvcmds.GetJsonCmd{},
kvcmds.LoadCsvCmd{},
kvcmds.DeleteCmd{},
kvcmds.DeletePrefixCmd{},
Expand Down Expand Up @@ -126,31 +130,62 @@ func main() {
shell.AutoHelp(false)

// register shell commands
for _, cmd := range RegisteredCmds {
for i, cmd := range RegisteredCmds {
handler := cmd.Handler()
//completer := cmd.Completer()
longhelp := cmd.LongHelp()
shell.AddCmd(&ishell.Cmd{
Name: cmd.Name(),
Help: cmd.Help(),
LongHelp: cmd.LongHelp(),
Aliases: cmd.Alias(),
Func: func(c *ishell.Context) {
ctx := context.WithValue(context.TODO(), "ishell", c)
if strings.ToLower(*clientLogLevel) == "debug" {
fmt.Fprintln(os.Stderr, color.YellowString("Input:"), c.RawArgs)
for _, arg := range c.Args {
fmt.Fprintln(os.Stderr, color.YellowString("Arg:"), arg)
}
fmt.Fprintf(os.Stderr, "\033[33mOutput:\033[0m\n")
}
if len(c.Args) > 0 && c.Args[0] == "--help" {
c.Println(longhelp)
return
}
handler(ctx)
},
})
// putjson is when i == 4. We need a multi-line input
if i == 4 {
shell.AddCmd(&ishell.Cmd{
Name: cmd.Name(),
Help: cmd.Help(),
LongHelp: cmd.LongHelp(),
Aliases: cmd.Alias(),
Func: func(c *ishell.Context) {
c.ShowPrompt(false)
defer c.ShowPrompt(true)
/* first type in a key, then enter.
screen print "Please enter a json value end with 'EOF'."
now input the multi-line json value. It has to end with "EOF".*/
c.Println("Please enter a json value end with 'EOF'.")
lines := c.ReadMultiLines("EOF")
/* connect the key and value with Json_separator to one long string.
the value of Json_separator and "EOF" should be strings that will not exist in the customer input.
separate the combined string later on by Json_separator and get the key and multi-line jsonvalue
*/
if len(c.Args) == 1 {
c.Args[0] = c.Args[0] + Json_separator + lines
c.RawArgs[1] = c.RawArgs[1] + " " + lines
}
ctx := context.WithValue(context.TODO(), "ishell", c)
fmt.Println(color.WhiteString("Input:"), strings.Join(c.RawArgs, " "))
fmt.Print("\n")
handler(ctx)
},
})
} else {
shell.AddCmd(&ishell.Cmd{
Name: cmd.Name(),
Help: cmd.Help(),
LongHelp: cmd.LongHelp(),
Aliases: cmd.Alias(),
Func: func(c *ishell.Context) {
ctx := context.WithValue(context.TODO(), "ishell", c)
if strings.ToLower(*clientLogLevel) == "debug" {
fmt.Fprintln(os.Stderr, color.YellowString("Input:"), c.RawArgs)
for _, arg := range c.Args {
fmt.Fprintln(os.Stderr, color.YellowString("Arg:"), arg)
}
fmt.Fprintf(os.Stderr, "\033[33mOutput:\033[0m\n")
}
if len(c.Args) > 0 && c.Args[0] == "--help" {
c.Println(longhelp)
return
}
handler(ctx)
},
})

}
}
shell.Run()
shell.Close()
Expand Down
1 change: 1 addition & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type Client interface {
GetPDClient() pd.Client

Put(ctx context.Context, kv KV) error
PutJson(ctx context.Context, kv KV) error
BatchPut(ctx context.Context, kv []KV) error

Get(ctx context.Context, k Key) (KV, error)
Expand Down
15 changes: 15 additions & 0 deletions client/rawkv_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func (c *rawkvClient) Put(ctx context.Context, kv KV) error {
return c.rawClient.Put(context.TODO(), kv.K, kv.V)
}

func (c *rawkvClient) PutJson(ctx context.Context, kv KV) error {
return c.rawClient.Put(context.TODO(), kv.K, kv.V)
}

func (c *rawkvClient) BatchPut(ctx context.Context, kvs []KV) error {
for _, kv := range kvs {
err := c.rawClient.Put(context.TODO(), kv.K[:], kv.V[:])
Expand All @@ -83,6 +87,17 @@ func (c *rawkvClient) Get(ctx context.Context, k Key) (KV, error) {
return KV{k, v}, nil
}

func (c *rawkvClient) GetJson(ctx context.Context, k Key) (KV, error) {
v, err := c.rawClient.Get(context.TODO(), k)
if err != nil {
return KV{}, err
}
if v == nil {
return KV{}, errors.New("key not found")
}
return KV{k, v}, nil
}

func (c *rawkvClient) Scan(ctx context.Context, prefix []byte) (KVS, int, error) {
scanOpts := utils.PropFromContext(ctx)

Expand Down
30 changes: 30 additions & 0 deletions client/txnkv_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,24 @@ func (c *txnkvClient) Put(ctx context.Context, kv KV) error {
return nil
}

func (c *txnkvClient) PutJson(ctx context.Context, kv KV) error {
tx, err := c.txnClient.Begin()
if err != nil {
return err
}

tx.Set(kv.K, kv.V)

err = tx.Commit(context.TODO())
if err != nil {
err = tx.Rollback()
if err != nil {
return err
}
}
return nil
}

func (c *txnkvClient) Scan(ctx context.Context, startKey []byte) (KVS, int, error) {
scanOpts := utils.PropFromContext(ctx)
tx, err := c.txnClient.Begin()
Expand Down Expand Up @@ -164,6 +182,18 @@ func (c *txnkvClient) Get(ctx context.Context, k Key) (KV, error) {
return KV{K: k, V: v}, nil
}

func (c *txnkvClient) GetJson(ctx context.Context, k Key) (KV, error) {
tx, err := c.txnClient.Begin()
if err != nil {
return KV{}, err
}
v, err := tx.Get(context.TODO(), k)
if err != nil {
return KV{}, err
}
return KV{K: k, V: v}, nil
}

func (c *txnkvClient) Delete(ctx context.Context, k Key) error {
tx, err := c.txnClient.Begin()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/magiconair/properties v1.8.0
github.com/manifoldco/promptui v0.8.0
github.com/olekukonko/tablewriter v0.0.5
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect
github.com/pingcap/go-ycsb v0.0.0-20210727125954-0c816a248fc3
github.com/pingcap/log v0.0.0-20210317133921-96f4fcab92a4
github.com/pkg/errors v0.9.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI=
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
Expand Down
66 changes: 66 additions & 0 deletions kvcmds/cmd_get_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package kvcmds

import (
"context"
"github.com/c4pt0r/tcli/utils"
"github.com/c4pt0r/tcli/client"
"fmt"
"encoding/json"
"github.com/oliveagle/jsonpath"
)

type GetJsonCmd struct{}

func (c GetJsonCmd) Name() string { return "getjson" }
func (c GetJsonCmd) Alias() []string { return []string{"getj", } }
func (c GetJsonCmd) Help() string {
return `getjson [key] [jsonpath]`
}

func (c GetJsonCmd) LongHelp() string {
return c.Help()
}

func (c GetJsonCmd) Handler() func(ctx context.Context) {
return func(ctx context.Context) {
utils.OutputWithElapse(func() error {
ic := utils.ExtractIshellContext(ctx)
if len(ic.Args) != 2 {
utils.Print(c.Help())
return nil
}

s := ic.RawArgs[1]
// it's a hex string literal
k, err := utils.GetStringLit(s)
if err != nil {
return err
}

kv, err := client.GetTiKVClient().Get(context.TODO(), client.Key(k))
if err != nil {
return err
}
//json_rawdata stores the jsonvalue of the key
json_rawdata := string(kv.V)

//json path starts with "$.", but there is no "$." from the customer input. Add on
edited_jsonpath := "$." + ic.RawArgs[2]

var json_data interface{}

//change the type of json_rawdata(string) to the type available for jsonpath, and store it to json_data
json.Unmarshal([]byte(json_rawdata), &json_data)

//find the jsonpath in the json_data and print
ans, err := jsonpath.JsonPathLookup(json_data, edited_jsonpath)
if err == nil {
fmt.Println(ans)
} else {
fmt.Println(err)
}

return nil
})
}
}
69 changes: 69 additions & 0 deletions kvcmds/cmd_put_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package kvcmds

import (
"context"
"fmt"
"github.com/c4pt0r/tcli/utils"
"github.com/c4pt0r/tcli/client"
"encoding/json"
"strings"
)

const Json_separator = "Xuan123#@!xuan"

type PutJsonCmd struct{}

func (c PutJsonCmd) Name() string { return "putjson" }
func (c PutJsonCmd) Alias() []string { return []string{"putjson", "putj"} }
func (c PutJsonCmd) Help() string {
return `putjson [key] [value]`
}
func (c PutJsonCmd) LongHelp() string {
return c.Help()
}

func (c PutJsonCmd) Handler() func(ctx context.Context) {
return func(ctx context.Context) {
utils.OutputWithElapse(func() error {
ic := utils.ExtractIshellContext(ctx)
// combine key and value together with the separator "Json_separator", so the length should be 1
if len(ic.Args) != 1 {
fmt.Println(c.LongHelp())
return nil
}

//use split to separate k and v by Json_separator
//now s[0] should be the key, and s[1] should be the value,with extra 'EOF' at the end
s := strings.Split(ic.Args[0], Json_separator)

//value now allowed to be empty, which is just "EOF"
if s[1] == "EOF" {
fmt.Println(c.LongHelp())
return nil
}

//take away the "EOF" from value
k := s[0]
v_and_EOF := strings.Split(s[1], "EOF")
v := v_and_EOF[0]

//checkJson if the value is valid jsonvalue
checkJson := json.Valid([]byte(v))

if checkJson == true {
fmt.Println("This is a valud json value. Successfully stored")
err := client.GetTiKVClient().Put(context.TODO(), client.KV{K: []byte(k), V: []byte(v)})
if err != nil {
fmt.Println("we have error")
return err
}
return nil
} else {
fmt.Println("This is an invalud json value. Please try again")
return nil
}

return nil
})
}
}