Skip to content

Commit

Permalink
Fixing issues with cli commands (-c)
Browse files Browse the repository at this point in the history
Kenneth Shaw committed Mar 28, 2017
1 parent 54067f4 commit 5858421
Showing 6 changed files with 79 additions and 68 deletions.
35 changes: 16 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -101,27 +101,24 @@ various queries.
A list of planned / in progress work:

### General
0. Show remote server version on connect
1. finish refactoring SQL specific code out of handler, rename handler to something more appropriate? cmd handler? metacmd?
2. Transaction wrapping / starts/commits / "-1" one transaction stuff
3. Fix include \i, \ir stuff
4. unix domain sockets for postgresql + detecting pg vs mysql
5. pager support
6. password prompts, -W cli option
7. .usqlpass file (same as .psqlpass)
8. SQL variables + environment
9. Proper table formatting + \pset
10. .usqlrc
11. More command line options
12. Fix meta command parsing when passed a quoted string ie, \echo " foo bar " should have all whitespace included in the parameter
13. Encoding support
14. fix table output
15. add support for managing multiple database connections simultaneously
1. Show remote server version on connect
2. Fix meta command parsing when passed a quoted string ie, \echo " foo
bar " should have all whitespace included in the parameter
3. fix table output
4. Transaction wrapping / starts/commits / "-1" one transaction stuff
5. pager + pipe / gexec/gset support
6. .usqlpass file (same as .psqlpass)
7. SQL variables + environment
8. Proper table formatting + \pset
9. .usqlrc
10. More command line options
11. add support for managing multiple database connections simultaneously
(@conn syntax, and a ~/.usqlconnections file, and ~/.usqlconfig) (maybe not
needed, if variable support works "as expected"?)
16. SQL completion (WIP)
17. syntax highlighting (WIP)
18. \encoding and environment/command line options to set encoding of input (to convert to utf-8 before feeding to SQL driver)
15. SQL completion (WIP)
16. syntax highlighting (WIP)
17. \encoding and environment/command line options to set encoding of input (to
convert to utf-8 before feeding to SQL driver)

#### Not important / "Nice to haves":
1. correct operation of interweaved -f/-c commands, ie: -f 1 -c 1 -c 2 -f 2 -f 3 -c 3 runs in the specified order
30 changes: 12 additions & 18 deletions handler/handler.go
Original file line number Diff line number Diff line change
@@ -88,7 +88,6 @@ func (h *Handler) Run() error {
for {
var err error
var execute bool
var exitWithErr error

// set prompt
if iactive {
@@ -98,11 +97,11 @@ func (h *Handler) Run() error {
// read next statement/command
cmd, params, err := h.buf.Next()
switch {
case !iactive && err == io.EOF:
execute, exitWithErr = true, io.EOF
case !iactive && err == nil:
execute = h.buf.Len != 0

case err == rline.ErrInterrupt:
h.buf.Reset()
h.buf.Reset(nil)
continue

case err != nil:
@@ -153,30 +152,24 @@ func (h *Handler) Run() error {
if execute || h.buf.Ready() || res.Exec != metacmd.ExecNone {
if h.buf.Len != 0 {
h.lastPrefix, h.last = h.buf.Prefix, h.buf.String()
h.buf.Reset()
h.buf.Reset(nil)
}

//log.Printf(">> PROCESS EXECUTE: (%) `%s`", last)
// log.Printf(">> PROCESS EXECUTE: (%s) `%s`", h.lastPrefix, h.last)
if h.last != "" && h.last != ";" {
err = h.Execute(stdout, h.lastPrefix, h.last)
if err != nil {
fmt.Fprintf(stderr, "error: %v", err)
fmt.Fprintln(stderr)
}
}

execute = false
}

if exitWithErr != nil {
return exitWithErr
}
}
}

// Reset resets the handler's statement buffer.
func (h *Handler) Reset() {
h.buf.Reset()
func (h *Handler) Reset(r []rune) {
h.buf.Reset(r)
h.last, h.lastPrefix = "", ""
}

@@ -308,9 +301,10 @@ func (h *Handler) Open(params ...string) error {
// connect
h.db, err = f(h.u.Driver, h.u.DSN)
if err != nil && !drivers.IsPasswordErr(h.u.Driver, err) {
return err
defer h.Close()
return h.WrapError(err)
} else if err == nil {
// do ping to force an error (if any)
// do ping to force error/check connection
err = h.db.Ping()
if err == nil {
return nil
@@ -319,7 +313,7 @@ func (h *Handler) Open(params ...string) error {

// bail without getting password
if !drivers.IsPasswordErr(h.u.Driver, err) || len(params) > 1 || !h.l.Interactive() {
h.Close()
defer h.Close()
return h.WrapError(err)
}

@@ -543,7 +537,7 @@ func (h *Handler) WrapError(err error) error {
return nil
}

if h.db != nil {
if h.u != nil {
// attempt to clean up and standardize errors
driver := h.u.Driver
if s, ok := drivers.Drivers[driver]; ok {
24 changes: 21 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
@@ -18,7 +18,8 @@ import (
)

func main() {
// circumvent all logic to determine if usql was built with support for a specific driver
// circumvent all logic to determine if usql was built with support for a
// specific driver
if len(os.Args) == 2 &&
strings.HasPrefix(os.Args[1], "--has-") &&
strings.HasSuffix(os.Args[1], "-support") {
@@ -57,7 +58,7 @@ func main() {
if err != nil && err != io.EOF && err != rline.ErrInterrupt {
fmt.Fprintf(os.Stderr, "error: %v\n", err)

// extra output for when the oracle driver is not available
// extra output for when a known driver is not available
if e, ok := err.(*handler.Error); ok && e.Err == handler.ErrDriverNotAvailable {
tag := e.Driver
if _, ok := drivers.KnownDrivers[tag]; !ok {
@@ -87,7 +88,7 @@ func run(args *Args, u *user.User) error {
}

// create input/output
l, err := rline.New(args.File, args.Out, args.HistoryFile)
l, err := rline.New(args.Commands, args.File, args.Out, args.HistoryFile)
if err != nil {
return err
}
@@ -102,5 +103,22 @@ func run(args *Args, u *user.User) error {
return err
}

// circumvent when provided commands
if len(args.Commands) != 0 {
return runCommands(args, h)
}

return h.Run()
}

// runCommands runs the cli passed commands (-c).
func runCommands(args *Args, h *handler.Handler) error {
for _, cmd := range args.Commands {
h.Reset([]rune(cmd))
err := h.Run()
if err != nil && err != io.EOF {
return err
}
}
return nil
}
14 changes: 7 additions & 7 deletions metacmd/cmds.go
Original file line number Diff line number Diff line change
@@ -75,10 +75,12 @@ func init() {
Name: "conninfo",
Desc: "display information about the current database connection",
Process: func(h Handler, _ string, _ []string) (Res, error) {
if u := h.URL(); u != nil {
out := h.IO().Stdout()
out := h.IO().Stdout()
if db, u := h.DB(), h.URL(); db != nil && u != nil {
fmt.Fprintf(out, text.ConnInfo, u.Driver, u.DSN)
fmt.Fprintln(out)
} else {
fmt.Fprintln(out, text.NotConnected)
}
return Res{}, nil
},
@@ -203,12 +205,10 @@ func init() {
s = buf.String()
}

n, err := env.EditFile(path, line, s)

// reset if no error
n, err := env.EditFile(path, line, s)
if err == nil {
buf.Reset()
buf.Feed(n)
buf.Reset(n)
}

return res, err
@@ -242,7 +242,7 @@ func init() {
Desc: "reset (clear) the query buffer",
Aliases: map[string]string{"reset": ""},
Process: func(h Handler, _ string, _ []string) (Res, error) {
h.Buf().Reset()
h.Buf().Reset(nil)
fmt.Fprintln(h.IO().Stdout(), text.QueryBufferReset)
return Res{}, nil
},
13 changes: 10 additions & 3 deletions rline/rline.go
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ func (l *Rline) ForceIntCyg(interactive, cygwin bool) {
}

// New creates a new readline input/output handler.
func New(in, out string, histfile string) (IO, error) {
func New(cmds []string, in, out string, histfile string) (IO, error) {
var err error

// determine if interactive
@@ -145,7 +145,9 @@ func New(in, out string, histfile string) (IO, error) {

// configure stdin
var stdin io.ReadCloser
if in != "" {
if len(cmds) != 0 {
interactive, cygwin = false, false
} else if in != "" {
stdin, err = os.OpenFile(in, os.O_RDONLY, 0)
if err != nil {
return nil, err
@@ -211,8 +213,13 @@ func New(in, out string, histfile string) (IO, error) {

closers = append(closers, l.Close)

n := l.Operation.Runes
if len(cmds) != 0 {
n = nil
}

return &Rline{
N: l.Operation.Runes,
N: n,
C: func() error {
for _, f := range closers {
f()
31 changes: 13 additions & 18 deletions stmt/stmt.go
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ const (
)

// Stmt is a reusable statement buffer that handles reading and parsing
// statements.
// SQL-like statements.
type Stmt struct {
// f is the rune source.
f func() ([]rune, error)
@@ -45,8 +45,7 @@ type Stmt struct {
ready bool
}

// New creates a new Stmt using the supplied rune source f, skipping errors
// equal to interruptErr.
// New creates a new Stmt using the supplied rune source f.
func New(f func() ([]rune, error), opts ...Option) *Stmt {
b := &Stmt{
f: f,
@@ -72,7 +71,7 @@ func (b *Stmt) Ready() bool {
}

// Reset resets the statement buffer.
func (b *Stmt) Reset() {
func (b *Stmt) Reset(r []rune) {
// reset buf
b.Buf, b.Len, b.Prefix = nil, 0, ""

@@ -87,6 +86,10 @@ func (b *Stmt) Reset() {

// ready state
b.ready = false

if r != nil {
b.r, b.rlen = r, len(r)
}
}

// lineend is the slice to use when appending a line.
@@ -102,12 +105,12 @@ var lineend = []rune{'\n'}
// called again only after any remaining collected runes have been processed.
//
// Example:
// stmt := stmt.New(runeSrc)
// buf := stmt.New(runeSrc)
// for {
// cmd, params, err := stmt.Next()
// cmd, params, err := buf.Next()
// if err { /* ... */ }
//
// execute, quit := stmt.Ready() || cmd == "g", cmd == "q"
// execute, quit := buf.Ready() || cmd == "g", cmd == "q"
//
// // process command ...
// switch cmd {
@@ -119,18 +122,18 @@ var lineend = []rune{'\n'}
// }
//
// if execute {
// s := stmt.String()
// s := buf.String()
// res, err := db.Query(s)
// /* handle database ... */
// stmt.Reset()
// buf.Reset(nil)
// }
// }
func (b *Stmt) Next() (string, []string, error) {
var err error
var i int

// no runes to process, grab more
if b.rlen == 0 {
var err error
b.r, err = b.f()
if err != nil {
return "", nil, err
@@ -293,14 +296,6 @@ func (b *Stmt) Append(r, sep []rune) {
b.Len = tlen
}

// Feed feeds the statement buffer, causing the statement buffer to parse r
// during successive calls to Next, until r has been fully read.
//
// Note: this is used to circumvent the provided rune source func.
func (b *Stmt) Feed(r []rune) {
b.r, b.rlen = r, len(r)
}

// AppendString is a util func wrapping Append.
func (b *Stmt) AppendString(s, sep string) {
b.Append([]rune(s), []rune(sep))

0 comments on commit 5858421

Please sign in to comment.