Commit 109b5f7b authored by Geofrey Ernest's avatar Geofrey Ernest Committed by GitHub

Merge pull request #171 from gernest/interactive

Add support for interactive mode in ql tool
parents 1a03cea9 f62eea38
...@@ -58,6 +58,7 @@ import ( ...@@ -58,6 +58,7 @@ import (
"log" "log"
"os" "os"
"regexp" "regexp"
"runtime"
"sort" "sort"
"strings" "strings"
"time" "time"
...@@ -84,22 +85,42 @@ func main() { ...@@ -84,22 +85,42 @@ func main() {
} }
} }
func do() (err error) { type config struct {
oDB := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.") db string
oFlds := flag.Bool("fld", false, "Show recordset's field names.") flds bool
oSchema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.") schema string
oTables := flag.String("tables", "", "If non empty, list matching table names and exit.") tables string
oTime := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.") time bool
help bool
interactive bool
}
func (c *config) parse() {
db := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.")
flds := flag.Bool("fld", false, "Show recordset's field names.")
schema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.")
tables := flag.String("tables", "", "If non empty, list matching table names and exit.")
time := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.")
help := flag.Bool("h", false, "Shows this help text.")
interactive := flag.Bool("i", false, "runs in interactive mode")
flag.Parse() flag.Parse()
c.flds = *flds
c.db = *db
c.schema = *schema
c.tables = *tables
c.time = *time
c.help = *help
c.interactive = *interactive
}
t0 := time.Now() func do() (err error) {
if *oTime { cfg := &config{}
defer func() { cfg.parse()
fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0)) if cfg.help || flag.NArg() == 0 && !cfg.interactive {
}() flag.PrintDefaults()
return nil
} }
db, err := ql.OpenFile(cfg.db, &ql.Options{CanCreate: true})
db, err := ql.OpenFile(*oDB, &ql.Options{CanCreate: true})
if err != nil { if err != nil {
return err return err
} }
...@@ -113,8 +134,85 @@ func do() (err error) { ...@@ -113,8 +134,85 @@ func do() (err error) {
err = ec err = ec
} }
}() }()
r := bufio.NewReader(os.Stdin)
o := bufio.NewWriter(os.Stdout)
if cfg.interactive {
for {
o.WriteString("ql> ")
o.Flush()
src, err := readSrc(cfg.interactive, r)
if err != nil {
return err
}
err = run(cfg, o, src, db)
if err != nil {
fmt.Fprintln(o, err)
o.Flush()
}
}
return nil
}
src, err := readSrc(cfg.interactive, r)
if err != nil {
return err
}
return run(cfg, o, src, db)
}
func readSrc(i bool, in *bufio.Reader) (string, error) {
if i {
return in.ReadString('\n')
}
var src string
switch n := flag.NArg(); n {
case 0:
b, err := ioutil.ReadAll(in)
if err != nil {
return "", err
}
if pat := *oSchema; pat != "" { src = string(b)
default:
a := make([]string, n)
for i := range a {
a[i] = flag.Arg(i)
}
src = strings.Join(a, " ")
}
return src, nil
}
func run(cfg *config, o *bufio.Writer, src string, db *ql.DB) (err error) {
defer o.Flush()
if cfg.interactive {
src = strings.TrimSpace(src)
if strings.HasPrefix(src, "\\") ||
strings.HasPrefix(src, ".") {
switch src {
case "\\clear", ".clear":
switch runtime.GOOS {
case "darwin", "linux":
fmt.Fprintln(o, "\033[H\033[2J")
default:
fmt.Fprintln(o, "clear not supported in this system")
}
return nil
case "\\q", "\\exit", ".q", ".exit":
// we make sure to close the database before exiting
db.Close()
os.Exit(1)
}
}
}
t0 := time.Now()
if cfg.time {
defer func() {
fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0))
}()
}
if pat := cfg.schema; pat != "" {
re, err := regexp.Compile(pat) re, err := regexp.Compile(pat)
if err != nil { if err != nil {
return err return err
...@@ -139,12 +237,12 @@ func do() (err error) { ...@@ -139,12 +237,12 @@ func do() (err error) {
} }
sort.Strings(r) sort.Strings(r)
if len(r) != 0 { if len(r) != 0 {
fmt.Println(strings.Join(r, "\n")) fmt.Fprintln(o, strings.Join(r, "\n"))
} }
return nil return nil
} }
if pat := *oTables; pat != "" { if pat := cfg.tables; pat != "" {
re, err := regexp.Compile(pat) re, err := regexp.Compile(pat)
if err != nil { if err != nil {
return err return err
...@@ -165,28 +263,11 @@ func do() (err error) { ...@@ -165,28 +263,11 @@ func do() (err error) {
} }
sort.Strings(r) sort.Strings(r)
if len(r) != 0 { if len(r) != 0 {
fmt.Println(strings.Join(r, "\n")) fmt.Fprintln(o, strings.Join(r, "\n"))
} }
return nil return nil
} }
var src string
switch n := flag.NArg(); n {
case 0:
b, err := ioutil.ReadAll(bufio.NewReader(os.Stdin))
if err != nil {
return err
}
src = string(b)
default:
a := make([]string, n)
for i := range a {
a[i] = flag.Arg(i)
}
src = strings.Join(a, " ")
}
src = "BEGIN TRANSACTION; " + src + "; COMMIT;" src = "BEGIN TRANSACTION; " + src + "; COMMIT;"
l, err := ql.Compile(src) l, err := ql.Compile(src)
if err != nil { if err != nil {
...@@ -206,13 +287,13 @@ func do() (err error) { ...@@ -206,13 +287,13 @@ func do() (err error) {
switch { switch {
case l.IsExplainStmt(): case l.IsExplainStmt():
return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
fmt.Println(data[0]) fmt.Fprintln(o, data[0])
return true, nil return true, nil
}) })
default: default:
return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { return rs[len(rs)-1].Do(cfg.flds, func(data []interface{}) (bool, error) {
fmt.Println(str(data)) fmt.Fprintln(o, str(data))
return true, nil return true, nil
}) })
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment