sq/cli/cmd_db_exec.go
Neil O'Toole fabc46c758
Switched more flags over to options mechanism (#394)
* Switched more flags over to options mechanism

* flag.IngestHeader

* flag.CSVEmptyAsNull and flag.CSVDelim

* flag.PingTimeout

* more flags

* flag.LogEnabled

* logging flags
2024-02-09 11:54:37 -07:00

150 lines
4.6 KiB
Go

package cli
import (
"fmt"
"strings"
"github.com/spf13/cobra"
"github.com/neilotoole/sq/cli/flag"
"github.com/neilotoole/sq/cli/run"
"github.com/neilotoole/sq/drivers/postgres"
"github.com/neilotoole/sq/libsq/core/errz"
"github.com/neilotoole/sq/libsq/core/execz"
"github.com/neilotoole/sq/libsq/core/lg"
"github.com/neilotoole/sq/libsq/core/lg/lga"
"github.com/neilotoole/sq/libsq/source"
"github.com/neilotoole/sq/libsq/source/drivertype"
)
func newDBExecCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "exec [@src] [--f SCRIPT.sql] [-c 'SQL'] [--print]",
Short: "Execute SQL script or command",
Long: `Execute SQL script or command using the db-native tool.
If no source is specified, the active source is used.
If --file is specified, the SQL is read from that file; otherwise if --command
is specified, that command string is used; otherwise the SQL commands are
read from stdin.
If --print or --print-long are specified, the SQL is not executed, but instead
the db-native command is printed to stdout. Note that the output will include DB
credentials.`,
Args: cobra.MaximumNArgs(1),
ValidArgsFunction: completeHandle(1, true),
RunE: execDBExec,
Example: ` # Execute query.sql on @sakila_pg
$ sq db exec @sakila_pg -f query.sql
# Same as above, but use stdin
$ sq db exec @sakila_pg < query.sql
# Execute a command string against the active source
$ sq db exec -c 'SELECT 777'
777
# Print the db-native command, but don't execute it
$ sq db exec -f query.sql --print
psql -d 'postgres://alice:abc123@db.acme.com:5432/sales' -f query.sql
# Execute against an alternative catalog or schema
$ sq db exec @sakila_pg --schema inventory.public -f query.sql`,
}
cmdMarkPlainStdout(cmd)
cmd.Flags().StringP(flag.DBExecFile, flag.DBExecFileShort, "", flag.DBExecFileUsage)
cmd.Flags().StringP(flag.DBExecCmd, flag.DBExecCmdShort, "", flag.DBExecCmdUsage)
cmd.MarkFlagsMutuallyExclusive(flag.DBExecFile, flag.DBExecCmd)
cmd.Flags().Bool(flag.DBPrintToolCmd, false, flag.DBPrintToolCmdUsage)
cmd.Flags().Bool(flag.DBPrintLongToolCmd, false, flag.DBPrintLongToolCmdUsage)
cmd.MarkFlagsMutuallyExclusive(flag.DBPrintToolCmd, flag.DBPrintLongToolCmd)
cmd.Flags().String(flag.ActiveSchema, "", flag.ActiveSchemaUsage)
panicOn(cmd.RegisterFlagCompletionFunc(flag.ActiveSchema,
activeSchemaCompleter{getActiveSourceViaArgs}.complete))
return cmd
}
func execDBExec(cmd *cobra.Command, args []string) error {
var (
ru = run.FromContext(cmd.Context())
src *source.Source
err error
// scriptFile is the (optional) path to the SQL file.
// If empty, cmdString or stdin is used.
scriptFile string
// scriptString is the optional SQL command string.
// If empty, scriptFile or stdin is used.
cmdString string
)
if src, err = getCmdSource(cmd, args); err != nil {
return err
}
errPrefix := "db exec: " + src.Handle
if cmdFlagChanged(cmd, flag.DBExecFile) {
if scriptFile = strings.TrimSpace(cmd.Flag(flag.DBExecFile).Value.String()); scriptFile == "" {
return errz.Errorf("%s: %s is specified, but empty", errPrefix, flag.DBExecFile)
}
}
if cmdFlagChanged(cmd, flag.DBExecCmd) {
if cmdString = strings.TrimSpace(cmd.Flag(flag.DBExecCmd).Value.String()); cmdString == "" {
return errz.Errorf("%s: %s is specified, but empty", errPrefix, flag.DBExecCmd)
}
}
if err = applySourceOptions(cmd, src); err != nil {
return err
}
var execCmd *execz.Cmd
switch src.Type { //nolint:exhaustive
case drivertype.Pg:
params := &postgres.ExecToolParams{
Verbose: OptVerbose.Get(src.Options),
ScriptFile: scriptFile,
CmdString: cmdString,
LongFlags: cmdFlagChanged(cmd, flag.DBPrintLongToolCmd),
}
execCmd, err = postgres.ExecCmd(src, params)
default:
return errz.Errorf("%s: not supported for %s", errPrefix, src.Type)
}
if err != nil {
return errz.Wrap(err, errPrefix)
}
execCmd.NoProgress = !OptProgress.Get(src.Options)
execCmd.Label = src.Handle + ": " + execCmd.Name
execCmd.Stdin = ru.Stdin
execCmd.Stdout = ru.Stdout
execCmd.Stderr = ru.ErrOut
execCmd.ErrPrefix = errPrefix
if cmdFlagBool(cmd, flag.DBPrintToolCmd) || cmdFlagBool(cmd, flag.DBPrintLongToolCmd) {
lg.FromContext(cmd.Context()).Info("Printing external cmd", lga.Cmd, execCmd)
s := execCmd.String()
_, err = fmt.Fprintln(ru.Out, s)
return errz.Err(err)
}
switch src.Type { //nolint:exhaustive
case drivertype.Pg:
lg.FromContext(cmd.Context()).Info("Executing external cmd", lga.Cmd, execCmd)
return execz.Exec(cmd.Context(), execCmd)
default:
return errz.Errorf("%s: not supported for %s", errPrefix, src.Type)
}
}