Skip to content

Commit 8e4d549

Browse files
committed
Better cli
1 parent 2585b57 commit 8e4d549

File tree

8 files changed

+313
-0
lines changed

8 files changed

+313
-0
lines changed

cli/cli.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package cli
2+
3+
import (
4+
"github.com/chdb-io/chdb-go/cli/history"
5+
"github.com/chdb-io/chdb-go/chdb"
6+
)
7+
8+
// CLI object of cli :)
9+
type CLI struct {
10+
history *history.History
11+
12+
Session *chdb.Session
13+
Multiline bool
14+
isMultilineInputStarted bool
15+
query string
16+
}
17+
18+
// New - returns CLI object
19+
func New(sess *chdb.Session, history *history.History, multiline bool) *CLI {
20+
return &CLI{
21+
history: history,
22+
Session: sess,
23+
Multiline: multiline,
24+
25+
isMultilineInputStarted: false,
26+
}
27+
}

cli/completer/completer.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package completer
2+
3+
import "github.com/c-bata/go-prompt"
4+
5+
// Completer object
6+
type Completer struct {
7+
}
8+
9+
// New - returns completer object
10+
func New() *Completer {
11+
return &Completer{}
12+
}
13+
14+
// Complete - returns suggestions for input
15+
func (c *Completer) Complete(d prompt.Document) []prompt.Suggest {
16+
return []prompt.Suggest{}
17+
}

cli/executor.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
"time"
8+
9+
"github.com/chdb-io/chdb-go/cli/history"
10+
)
11+
12+
var exitCodes = [...]string{
13+
"exit",
14+
"quit",
15+
"logout",
16+
"учше",
17+
"йгше",
18+
"дщпщге",
19+
"exit;",
20+
"quit;",
21+
"logout;",
22+
"учшеж",
23+
"йгшеж",
24+
"дщпщгеж",
25+
"q",
26+
"й",
27+
"Q",
28+
":q",
29+
"Й",
30+
"Жй",
31+
}
32+
33+
// Executor - exec query and write it to history + checking for one of quit commands.
34+
func (c *CLI) Executor(s string) {
35+
if !c.isMultilineInputStarted {
36+
for _, code := range exitCodes {
37+
if s == code {
38+
fmt.Println("Bye.")
39+
os.Exit(0)
40+
return
41+
}
42+
}
43+
44+
if strings.Contains(s, "\\") {
45+
mToSQL, err := c.MetaToSQL(s)
46+
if err != nil {
47+
fmt.Println(err)
48+
return
49+
}
50+
51+
s = mToSQL
52+
}
53+
}
54+
55+
if c.Multiline {
56+
if strings.TrimSpace(s) != "" {
57+
if strings.HasSuffix(s, ";") {
58+
c.query += s
59+
60+
c.isMultilineInputStarted = false
61+
} else {
62+
c.query += s + " "
63+
64+
c.isMultilineInputStarted = true
65+
}
66+
}
67+
} else {
68+
c.query = s
69+
}
70+
71+
if !c.isMultilineInputStarted {
72+
if err := c.history.Write(&history.Row{
73+
CreatedAt: time.Now(),
74+
Query: c.query,
75+
}); err != nil {
76+
fmt.Println(err)
77+
}
78+
79+
trimedQuery := strings.TrimSpace(c.query)
80+
if len(trimedQuery) == 0 {
81+
fmt.Println("")
82+
} else {
83+
data := c.Session.Query(trimedQuery, "Pretty")
84+
fmt.Println(data)
85+
}
86+
87+
c.query = ""
88+
}
89+
}

cli/history/history.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package history
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"strings"
9+
"time"
10+
)
11+
12+
// ClickHouseDateFormat - representation of date in Clickhouse,
13+
// like RFC3339 but without timezone and with milliseconds
14+
const ClickHouseDateFormat = "2006-01-02 15:04:05.000"
15+
16+
// History object
17+
type History struct {
18+
file *os.File
19+
}
20+
21+
// Row of history
22+
type Row struct {
23+
CreatedAt time.Time
24+
Query string
25+
}
26+
27+
// New history object from file
28+
func New(path string) (*History, error) {
29+
var file *os.File
30+
var err error
31+
32+
file, err = os.OpenFile(path, os.O_RDWR, os.ModePerm)
33+
if err != nil {
34+
if errors.Is(err, os.ErrNotExist) {
35+
file, err = os.Create(path)
36+
if err != nil {
37+
return nil, err
38+
}
39+
} else {
40+
return nil, err
41+
}
42+
}
43+
44+
return &History{file: file}, err
45+
}
46+
47+
// Close history file
48+
func (h *History) Close() error {
49+
return h.file.Close()
50+
}
51+
52+
// Read history from file
53+
func (h *History) Read() ([]*Row, error) {
54+
var rows []*Row
55+
56+
scanner := bufio.NewScanner(h.file)
57+
58+
for scanner.Scan() {
59+
text := scanner.Text()
60+
61+
if strings.Contains(text, "###") {
62+
var row Row
63+
64+
dateStr := strings.TrimPrefix(text, "### ")
65+
date, err := time.Parse("2006-01-02 15:04:05", dateStr)
66+
if err != nil {
67+
return nil, err
68+
}
69+
70+
row.CreatedAt = date
71+
72+
scanner.Scan()
73+
row.Query = scanner.Text()
74+
75+
rows = append(rows, &row)
76+
}
77+
}
78+
79+
if err := scanner.Err(); err != nil {
80+
return nil, err
81+
}
82+
83+
return rows, nil
84+
}
85+
86+
// Write row creation date
87+
func (h *History) Write(row *Row) error {
88+
_, err := h.file.WriteString(fmt.Sprintf("### %s\n%s\n", row.CreatedAt.Format(ClickHouseDateFormat), row.Query))
89+
90+
return err
91+
}
92+
93+
// RowsToStrArr - convert rows to slice of strings
94+
func (h *History) RowsToStrArr(rows []*Row) []string {
95+
historyArr := make([]string, 0, len(rows))
96+
97+
for _, row := range rows {
98+
historyArr = append(historyArr, row.Query)
99+
}
100+
101+
return historyArr
102+
}

cli/keybind.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package cli
2+
3+
import (
4+
"github.com/c-bata/go-prompt"
5+
)
6+
7+
// MultilineControl is a multiline toggle.
8+
func (c *CLI) MultilineControl(buffer *prompt.Buffer) {
9+
if c.isMultilineInputStarted {
10+
return
11+
}
12+
13+
c.Multiline = !c.Multiline
14+
}

cli/liveprefix.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package cli
2+
3+
// MultilineCLIPrefix shows that multiline input is activated
4+
const MultilineCLIPrefix = ":-] "
5+
6+
// GetLivePrefixState - returns prefix and multiline input current state (true/false)
7+
func (c *CLI) GetLivePrefixState() (string, bool) {
8+
return MultilineCLIPrefix, c.isMultilineInputStarted
9+
}

cli/metatosql.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"strings"
6+
)
7+
8+
// ErrInvalidMetaCommand error.
9+
var ErrInvalidMetaCommand = errors.New("incorrect meta-command")
10+
11+
// ErrArgumentNotProvided error.
12+
var ErrArgumentNotProvided = errors.New("argument to meta-command doesnt provided")
13+
14+
// MetaToSQL convert meta-command to SQL expression
15+
func (c *CLI) MetaToSQL(metaCommand string) (string, error) {
16+
var expression string
17+
18+
metaCommand = strings.TrimPrefix(metaCommand, "\\")
19+
20+
metaCommandArr := strings.Split(metaCommand, " ")
21+
22+
switch metaCommandArr[0] {
23+
case "dt":
24+
if len(metaCommandArr) >= 2 {
25+
expression = "SHOW TABLES FROM " + metaCommandArr[1]
26+
} else {
27+
return "", ErrArgumentNotProvided
28+
}
29+
case "l":
30+
expression = "SHOW DATABASES"
31+
default:
32+
return "", ErrInvalidMetaCommand
33+
}
34+
35+
return expression, nil
36+
}

cli/util.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"strings"
6+
"io/ioutil"
7+
)
8+
9+
// GetCurrentDB from chDB
10+
func (c *CLI) GetCurrentDB(ctx context.Context) string {
11+
// read current database from path "c.Session.Path()/default_database"
12+
filePath := c.Session.Path() + "/default_database"
13+
content, err := ioutil.ReadFile(filePath)
14+
if err != nil {
15+
return ""
16+
}
17+
18+
return strings.TrimSpace(string(content))
19+
}

0 commit comments

Comments
 (0)