1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
package metacmd
import (
"context"
"database/sql"
"io"
"os/user"
"strings"
"time"
"github.com/xo/dburl"
"github.com/xo/usql/drivers"
"github.com/xo/usql/drivers/metadata"
"github.com/xo/usql/env"
"github.com/xo/usql/rline"
"github.com/xo/usql/stmt"
"github.com/xo/usql/text"
)
// Handler is the shared interface for a command handler.
type Handler interface {
// IO handles the handler's IO.
IO() rline.IO
// User returns the current user.
User() *user.User
// URL returns the current database URL.
URL() *dburl.URL
// DB returns the current database connection.
DB() drivers.DB
// Last returns the last executed query.
Last() string
// LastRaw returns the last raw (non-interpolated) query.
LastRaw() string
// Buf returns the current query buffer.
Buf() *stmt.Stmt
// Reset resets the last and current query buffer.
Reset([]rune)
// Bind binds query parameters.
Bind([]interface{})
// Open opens a database connection.
Open(context.Context, ...string) error
// Close closes the current database connection.
Close() error
// ChangePassword changes the password for a user.
ChangePassword(string) (string, error)
// ReadVar reads a variable of a specified type.
ReadVar(string, string) (string, error)
// Include includes a file.
Include(string, bool) error
// Begin begins a transaction.
Begin(*sql.TxOptions) error
// Commit commits the current transaction.
Commit() error
// Rollback aborts the current transaction.
Rollback() error
// Highlight highlights the statement.
Highlight(io.Writer, string) error
// GetTiming mode.
GetTiming() bool
// SetTiming mode.
SetTiming(bool)
// GetOutput writer.
GetOutput() io.Writer
// SetOutput writer.
SetOutput(io.WriteCloser)
// MetadataWriter retrieves the metadata writer for the handler.
MetadataWriter(context.Context) (metadata.Writer, error)
// Print formats according to a format specifier and writes to handler's standard output.
Print(string, ...interface{})
}
// Runner is a runner interface type.
type Runner interface {
Run(Handler) (Option, error)
}
// RunnerFunc is a type wrapper for a single func satisfying Runner.Run.
type RunnerFunc func(Handler) (Option, error)
// Run satisfies the Runner interface.
func (f RunnerFunc) Run(h Handler) (Option, error) {
return f(h)
}
// ExecType represents the type of execution requested.
type ExecType int
const (
// ExecNone indicates no execution.
ExecNone ExecType = iota
// ExecOnly indicates plain execution only (\g).
ExecOnly
// ExecPipe indicates execution and piping results (\g |file)
ExecPipe
// ExecSet indicates execution and setting the resulting columns as
// variables (\gset).
ExecSet
// ExecExec indicates execution and executing the resulting rows (\gexec).
ExecExec
// ExecCrosstab indicates execution using crosstabview (\crosstabview).
ExecCrosstab
// ExecWatch indicates repeated execution with a fixed time interval.
ExecWatch
)
// Option contains parsed result options of a metacmd.
type Option struct {
// Quit instructs the handling code to quit.
Quit bool
// Exec informs the handling code of the type of execution.
Exec ExecType
// Params are accompanying string parameters for execution.
Params map[string]string
// Crosstab are the crosstab column parameters.
Crosstab []string
// Watch is the watch duration interval.
Watch time.Duration
}
func (opt *Option) ParseParams(params []string, defaultKey string) error {
if opt.Params == nil {
opt.Params = make(map[string]string, len(params))
}
formatOptions := false
for i, param := range params {
if len(param) == 0 {
continue
}
if !formatOptions {
if param[0] == '(' {
formatOptions = true
} else {
opt.Params[defaultKey] = strings.Join(params[i:], " ")
return nil
}
}
parts := strings.SplitN(param, "=", 2)
if len(parts) == 1 {
return text.ErrInvalidFormatOption
}
opt.Params[strings.TrimLeft(parts[0], "(")] = strings.TrimRight(parts[1], ")")
if formatOptions && param[len(param)-1] == ')' {
formatOptions = false
}
}
return nil
}
// Params wraps metacmd parameters.
type Params struct {
// Handler is the process handler.
Handler Handler
// Name is the name of the metacmd.
Name string
// Params are the actual statement parameters.
Params *stmt.Params
// Option contains resulting command execution options.
Option Option
}
// Get returns the next command parameter, using env.Unquote to decode quoted
// strings.
func (p *Params) Get(exec bool) (string, error) {
_, v, err := p.Params.Get(env.Unquote(
p.Handler.User(),
exec,
env.All(),
))
if err != nil {
return "", err
}
return v, nil
}
// GetOK returns the next command parameter, using env.Unquote to decode quoted
// strings.
func (p *Params) GetOK(exec bool) (bool, string, error) {
return p.Params.Get(env.Unquote(
p.Handler.User(),
exec,
env.All(),
))
}
// GetOptional returns the next command parameter, using env.Unquote to decode
// quoted strings, returns true when the value is prefixed with a "-", along
// with the value sans the "-" prefix. Otherwise returns false and the value.
func (p *Params) GetOptional(exec bool) (bool, string, error) {
v, err := p.Get(exec)
if err != nil {
return false, "", err
}
if len(v) > 0 && v[0] == '-' {
return true, v[1:], nil
}
return false, v, nil
}
// GetAll gets all remaining command parameters using env.Unquote to decode
// quoted strings.
func (p *Params) GetAll(exec bool) ([]string, error) {
return p.Params.GetAll(env.Unquote(
p.Handler.User(),
exec,
env.All(),
))
}
// GetRaw gets the remaining command parameters as a raw string.
//
// Note: no other processing is done to interpolate variables or to decode
// string values.
func (p *Params) GetRaw() string {
return p.Params.GetRaw()
}
|