Dolt SQL shell. Also added methods to construct a VerboseError from an error, and to get the dolt dir from an env.

This commit is contained in:
Zach Musgrave
2019-05-14 13:06:38 -07:00
parent fefe826928
commit 0c854ed4c4
9 changed files with 117 additions and 35 deletions

View File

@@ -107,10 +107,10 @@ teardown() {
[[ "$output" =~ "9" ]] || false
run dolt sql -q "insert into test (c1,c3,c5) values (50,55,60)"
[ "$status" -eq 1 ]
[ "$output" = "Error inserting rows: [one or more primary key columns missing from insert statement]" ]
[ "$output" = "Error inserting rows: one or more primary key columns missing from insert statement" ]
run dolt sql -q "insert into test (pk,c1,c2,c3,c4,c5,c6) values (10,1,1,1,1,1,1)"
[ "$status" -eq 1 ]
[ "$output" = "Error inserting rows: [Unknown column: 'c6']" ]
[ "$output" = "Error inserting rows: Unknown column: 'c6'" ]
run dolt sql -q "insert into test (pk,c1,c2,c3,c4,c5) values (0,6,6,6,6,6)"
[ "$status" -eq 1 ]
[[ "$output" =~ "cannot insert existing row" ]] || false

View File

@@ -89,5 +89,5 @@ teardown() {
[[ ! "$output" =~ "6" ]] || false
run dolt sql -q "insert into test (pk1,c1,c2,c3,c4,c5) values (0,6,6,6,6,6)"
[ "$status" -eq 1 ]
[ "$output" = "Error inserting rows: [row constraint failed]" ] || false
[ "$output" = "Error inserting rows: row constraint failed" ] || false
}

View File

@@ -78,9 +78,6 @@ teardown() {
}
@test "dolt sql in a new repository" {
run dolt sql
[ "$status" -eq 1 ]
[ "${lines[0]}" = "usage: dolt sql [options] -q query_string" ]
run dolt sql -q "select * from test"
[ "$status" -eq 1 ]
[[ "$output" = "Unknown table 'test'" ]] || false

View File

@@ -4,7 +4,9 @@ import (
"context"
"errors"
"fmt"
"github.com/abiosoft/readline"
"github.com/fatih/color"
"github.com/liquidata-inc/ishell"
"github.com/liquidata-inc/ld/dolt/go/cmd/dolt/cli"
"github.com/liquidata-inc/ld/dolt/go/cmd/dolt/errhand"
"github.com/liquidata-inc/ld/dolt/go/libraries/doltcore/doltdb"
@@ -21,6 +23,8 @@ import (
"github.com/liquidata-inc/ld/dolt/go/libraries/utils/argparser"
"github.com/liquidata-inc/ld/dolt/go/libraries/utils/iohelp"
"github.com/xwb1989/sqlparser"
"path/filepath"
"strings"
)
var sqlShortDesc = "EXPERIMENTAL: Runs a SQL query"
@@ -35,6 +39,9 @@ var fwtStageName = "fwt"
const (
queryFlag = "query"
welcomeMsg = `# Welcome to the DoltSQL shell.
# Statements must be terminated with ';'.
# "exit" or "quit" (or Ctrl-D) to exit.`
)
func Sql(commandStr string, args []string, dEnv *env.DoltEnv) int {
@@ -47,60 +54,109 @@ func Sql(commandStr string, args []string, dEnv *env.DoltEnv) int {
query, ok := apr.GetValue(queryFlag)
if ok {
err := processInput(dEnv, query, usage)
// run a single command and exit
err := processQuery(query, dEnv)
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
// start the doltsql REPL
// shellConf := readline.Config{
// Prompt: "doltsql>",
// Stdout: cli.CliOut,
// Stderr: cli.CliOut,
// }
// shell := ishell.NewWithConfig(&shellConf)
// shell.Run()
_ = iohelp.WriteLine(cli.CliOut, welcomeMsg)
// start the doltsql shell
historyFile := filepath.Join(dEnv.GetDoltDir(), ".sqlhistory")
rlConf := readline.Config{
Prompt: "doltsql> ",
Stdout: cli.CliOut,
Stderr: cli.CliOut,
HistoryFile: historyFile,
HistoryLimit: 500,
HistorySearchFold: true,
DisableAutoSaveHistory: true,
}
shellConf := ishell.UninterpretedConfig{
ReadlineConfig: &rlConf,
QuitKeywords: []string {
"quit", "exit", "quit()", "exit()",
},
LineTerminator: ";",
}
shell := ishell.NewUninterpreted(&shellConf)
shell.SetMultiPrompt( " -> ")
shell.EOF(func(c *ishell.Context) {
c.Stop()
})
shell.Interrupt(func(c *ishell.Context, count int, input string) {
if count > 1 {
c.Stop()
} else {
c.Println("Received SIGINT. Interrupt again to exit, or use ^D, quit, or exit")
}
})
shell.Uninterpreted(func(c *ishell.Context) {
query = c.Args[0]
if len(strings.TrimSpace(query)) == 0 {
return
}
if err := processQuery(query, dEnv); err != nil {
shell.Println(color.RedString(err.Error()))
}
// TODO: there's a bug in the readline library when editing multi-line history entries.
// Longer term we need to switch to a new readline library, like in this bug:
// https://github.com/cockroachdb/cockroach/issues/15460
// For now, we store all history entries as single-line strings to avoid the issue.
singleLine := strings.ReplaceAll(query, "\n", " ")
if err := shell.AddHistory(singleLine); err != nil {
// TODO: handle better, like by turning off history writing for the rest of the session
shell.Println(color.RedString(err.Error()))
}
})
shell.Run()
_ = iohelp.WriteLine(cli.CliOut, "Bye")
return 0
}
// Processes a single query and returns any error encountered
func processInput(dEnv *env.DoltEnv, input string, usage cli.UsagePrinter) error {
sqlStatement, err := sqlparser.Parse(input)
func processQuery(query string, dEnv *env.DoltEnv) error {
sqlStatement, err := sqlparser.Parse(query)
if err != nil {
return errFmt("Error parsing SQL: %v.", err.Error())
}
root, verr := GetWorkingWithVErr(dEnv)
if verr != nil {
return errFmt(verr.Verbose())
return verr
}
switch s := sqlStatement.(type) {
case *sqlparser.Select:
return sqlSelect(root, s, input)
return sqlSelect(root, s)
case *sqlparser.Insert:
return sqlInsert(dEnv, root, s, input, usage)
return sqlInsert(dEnv, root, s, query)
case *sqlparser.Update:
return sqlUpdate(dEnv, root, s, input, usage)
return sqlUpdate(dEnv, root, s, query)
case *sqlparser.Delete:
return sqlDelete(dEnv, root, s, input, usage)
return sqlDelete(dEnv, root, s, query)
case *sqlparser.DDL:
_, err := sqlparser.ParseStrictDDL(input)
_, err := sqlparser.ParseStrictDDL(query)
if err != nil {
return errFmt("Error parsing SQL: %v.", err.Error())
}
return sqlDDL(dEnv, root, s, input, usage)
return sqlDDL(dEnv, root, s, query)
default:
return errFmt("Unhandled SQL statement: %v.", input)
return errFmt("Unhandled SQL statement: %v.", query)
}
}
// Executes a SQL select statement and prints the result to the CLI.
func sqlSelect(root *doltdb.RootValue, s *sqlparser.Select, query string) error {
func sqlSelect(root *doltdb.RootValue, s *sqlparser.Select) error {
p, statement, err := sql.BuildSelectQueryPipeline(context.TODO(), root, s)
if err != nil {
cli.PrintErrln(color.RedString(err.Error()))
return err
}
@@ -144,7 +200,7 @@ func sqlSelect(root *doltdb.RootValue, s *sqlparser.Select, query string) error
}
// Executes a SQL insert statement and prints the result to the CLI.
func sqlInsert(dEnv *env.DoltEnv, root *doltdb.RootValue, stmt *sqlparser.Insert, query string, usage cli.UsagePrinter) error {
func sqlInsert(dEnv *env.DoltEnv, root *doltdb.RootValue, stmt *sqlparser.Insert, query string) error {
result, err := sql.ExecuteInsert(context.Background(), dEnv.DoltDB, root, stmt, query)
if err != nil {
return errFmt("Error inserting rows: %v", err.Error())
@@ -166,7 +222,7 @@ func sqlInsert(dEnv *env.DoltEnv, root *doltdb.RootValue, stmt *sqlparser.Insert
}
// Executes a SQL update statement and prints the result to the CLI.
func sqlUpdate(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Update, query string, usage cli.UsagePrinter) error {
func sqlUpdate(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Update, query string) error {
result, err := sql.ExecuteUpdate(context.Background(), dEnv.DoltDB, root, update, query)
if err != nil {
return errFmt("Error during update: %v", err.Error())
@@ -185,7 +241,7 @@ func sqlUpdate(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Upda
}
// Executes a SQL delete statement and prints the result to the CLI.
func sqlDelete(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Delete, query string, usage cli.UsagePrinter) error {
func sqlDelete(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Delete, query string) error {
result, err := sql.ExecuteDelete(context.Background(), dEnv.DoltDB, root, update, query)
if err != nil {
return errFmt("Error during update: %v", err.Error())
@@ -200,7 +256,7 @@ func sqlDelete(dEnv *env.DoltEnv, root *doltdb.RootValue, update *sqlparser.Dele
}
// Executes a SQL DDL statement (create, update, etc.) and prints the result to the CLI.
func sqlDDL(dEnv *env.DoltEnv, root *doltdb.RootValue, ddl *sqlparser.DDL, query string, usage cli.UsagePrinter) error {
func sqlDDL(dEnv *env.DoltEnv, root *doltdb.RootValue, ddl *sqlparser.DDL, query string) error {
switch ddl.Action {
case sqlparser.CreateStr:
newRoot, _, err := sql.ExecuteCreate(context.Background(), dEnv.DoltDB, root, ddl, query)

View File

@@ -67,7 +67,6 @@ func TestBadInput(t *testing.T) {
args []string
expectedRes int
}{
{"no input", []string{""}, 1},
{"no query", []string{"-q", ""}, 1},
}
for _, test := range tests {

View File

@@ -102,7 +102,7 @@ func VerboseErrorFromError(err error) VerboseError {
return verr
}
builder := &DErrorBuilder{err.Error(), "", err, false}
builder := &DErrorBuilder{err.Error(), "", nil, false}
return builder.Build()
}

View File

@@ -2,6 +2,7 @@ module github.com/liquidata-inc/ld/dolt/go
require (
cloud.google.com/go v0.35.1 // indirect
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/attic-labs/noms v0.0.0-20181127201811-95e8b35cc96f
github.com/aws/aws-sdk-go v1.16.26 // indirect
@@ -13,6 +14,7 @@ require (
github.com/google/go-cmp v0.2.0
github.com/google/uuid v1.1.0
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 // indirect
github.com/liquidata-inc/ishell v0.0.0-20190514193646-693241f1f2a0
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d
github.com/pkg/errors v0.8.1
github.com/skratchdot/open-golang v0.0.0-20190104022628-a2dfa6d0dab6
@@ -31,3 +33,5 @@ require (
replace github.com/attic-labs/noms => github.com/liquidata-inc/noms v0.0.0-20190506171537-518b7edcd73f
replace github.com/xwb1989/sqlparser => github.com/liquidata-inc/sqlparser v0.9.3
//replace github.com/liquidata-inc/ishell => ../../../ishell

View File

@@ -11,6 +11,10 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
git.apache.org/thrift.git v0.0.0-20181218151757-9b75e4fe745a/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw=
github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg=
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLbTdr1RceuZgSFDQ7U15ITERUGrUORx8=
github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -24,6 +28,10 @@ github.com/aws/aws-sdk-go v1.16.26 h1:GWkl3rkRO/JGRTWoLLIqwf7AWC4/W/1hMOUZqmX0js
github.com/aws/aws-sdk-go v1.16.26/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd h1:XtfPmj9tQRilnrEmI1HjQhxXWRhEM+m8CACtaMJE/kM=
github.com/cjbassi/drawille-go v0.0.0-20190126131713-27dc511fe6fd/go.mod h1:vjcQJUZJYD3MeVGhtZXSMnCHfUNZxsyYzJt90eCYxK4=
github.com/clbanning/mxj v1.8.3/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
@@ -39,6 +47,8 @@ github.com/edsrzf/mmap-go v1.0.0-20181222142022-904c4ced31cd h1:d4FJbBWRop8iXtFb
github.com/edsrzf/mmap-go v1.0.0-20181222142022-904c4ced31cd/go.mod h1:W3m91qexYIu40kcj8TLXNUSTCKprH8UQ3GgH5/Xyfc0=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI=
github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -106,14 +116,21 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/liquidata-inc/ishell v0.0.0-20190514193646-693241f1f2a0 h1:phMgajKClMUiIr+hF2LGt8KRuUa2Vd2GI1sNgHgSXoU=
github.com/liquidata-inc/ishell v0.0.0-20190514193646-693241f1f2a0/go.mod h1:YC1rI9k5gx8D02ljlbxDfZe80s/iq8bGvaaQsvR+qxs=
github.com/liquidata-inc/noms v0.0.0-20190506171537-518b7edcd73f h1:Z9dfDI1SCNWSxM+Y8HYueFMSDeW0Lzrw0Img81+427s=
github.com/liquidata-inc/noms v0.0.0-20190506171537-518b7edcd73f/go.mod h1:3M7C9+Co0TI0lJ7TICfOIZSYBY5C0bt2VGaZTx6tfs4=
github.com/liquidata-inc/sqlparser v0.9.3 h1:lAQpKBSHYG4EWI7RZiQvuSwBqpd45wzmgTtcxEGXaCk=
github.com/liquidata-inc/sqlparser v0.9.3/go.mod h1:lEqKV1oJUXiTfUjigM/8APQJFNSTHYYkJk+NGkWSgOU=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -230,6 +247,7 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181221143128-b4a75ba826a6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=

View File

@@ -78,6 +78,14 @@ func (dEnv *DoltEnv) HasDoltDataDir() bool {
return dEnv.hasDoltDataDir("./")
}
// GetDoltDir returns the path to the .dolt directory
func (dEnv *DoltEnv) GetDoltDir() string {
if !dEnv.HasDoltDataDir() {
panic("No dolt dir")
}
return filepath.Join("./", doltdb.DoltDir)
}
func (dEnv *DoltEnv) hasDoltDir(path string) bool {
exists, isDir := dEnv.FS.Exists(filepath.Join(path, doltdb.DoltDir))
return exists && isDir