mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-29 11:13:32 +03:00
Refactored verbose/header flags; inspect cmd now less verbose by default (#111)
* Refactored verbose/header flags; inspect cmd now less verbose by default * Updated changelog * README update
This commit is contained in:
parent
e425da528f
commit
703f6d2427
14
CHANGELOG.md
14
CHANGELOG.md
@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- `--verbose` flag is now global
|
||||
|
||||
### Changed
|
||||
|
||||
- `sq inspect` shows less output by default (use `-v` to restore previous behavior)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `sq inspect` can now deal with Postgres sources that have null values for constraint fields
|
||||
|
||||
## [v0.16.0] - 2022-11-20
|
||||
|
||||
### Added
|
||||
|
19
README.md
19
README.md
@ -142,17 +142,16 @@ But we're flying a bit blind here: how did we know about the `actor` table?
|
||||
`sq inspect` is your friend (output abbreviated):
|
||||
|
||||
```sh
|
||||
$ sq inspect
|
||||
HANDLE DRIVER NAME FQ NAME SIZE TABLES LOCATION
|
||||
@sakila_sl3 sqlite3 sakila.db sakila.db/main 5.6MB 21 sqlite3:///root/sakila.db
|
||||
HANDLE DRIVER NAME FQ NAME SIZE TABLES LOCATION
|
||||
@sakila_sl3 sqlite3 sakila.db sakila.db/main 5.6MB 21 sqlite3:/Users/neilotoole/work/sq/sq/drivers/sqlite3/testdata/sakila.db
|
||||
|
||||
TABLE ROWS TYPE SIZE NUM COLS COL NAMES COL TYPES
|
||||
actor 200 table - 4 actor_id, first_name, last_name, last_update numeric, VARCHAR(45), VARCHAR(45), TIMESTAMP
|
||||
address 603 table - 8 address_id, address, address2, district, city_id, postal_code, phone, last_update int, VARCHAR(50), VARCHAR(50), VARCHAR(20), INT, VARCHAR(10), VARCHAR(20), TIMESTAMP
|
||||
category 16 table - 3 category_id, name, last_update
|
||||
TABLE ROWS COL NAMES
|
||||
actor 200 actor_id, first_name, last_name, last_update
|
||||
address 603 address_id, address, address2, district, city_id, postal_code, phone, last_update
|
||||
category 16 category_id, name, last_update
|
||||
```
|
||||
|
||||
Use `--json` (`-j`) to output in JSON (output abbreviated):
|
||||
Use the `--verbose` (`-v`) flag to see more detail. And use `--json` (`-j`) to output in JSON (output abbreviated):
|
||||
|
||||
```shell
|
||||
$ sq inspect -j
|
||||
@ -206,7 +205,7 @@ category.csv customer.csv film_actor.csv film_text.csv payment.csv sale
|
||||
Note that you can also inspect an individual table:
|
||||
|
||||
```sh
|
||||
$ sq inspect @sakila_sl3.actor
|
||||
$ sq inspect -v @sakila_sl3.actor
|
||||
TABLE ROWS TYPE SIZE NUM COLS COL NAMES COL TYPES
|
||||
actor 200 table - 4 actor_id, first_name, last_name, last_update numeric, VARCHAR(45), VARCHAR(45), TIMESTAMP
|
||||
|
||||
@ -242,7 +241,7 @@ Now, execute the same query, but this time `sq` inserts the results into a new t
|
||||
$ sq @xl_demo_xlsx.person --insert @sakila_sl3.person
|
||||
Inserted 7 rows into @sakila_sl3.person
|
||||
|
||||
$ sq inspect @sakila_sl3.person
|
||||
$ sq inspect -v @sakila_sl3.person
|
||||
TABLE ROWS TYPE SIZE NUM COLS COL NAMES COL TYPES
|
||||
person 7 table - 4 uid, username, email, address_id INTEGER, TEXT, TEXT, INTEGER
|
||||
|
||||
|
46
cli/cli.go
46
cli/cli.go
@ -198,6 +198,7 @@ func newCommandTree(rc *RunContext) (rootCmd *cobra.Command) {
|
||||
// The behavior of cobra in this regard seems to have
|
||||
// changed? This particular incantation currently does the trick.
|
||||
rootCmd.Flags().Bool(flagHelp, false, "Show sq help")
|
||||
|
||||
rootCmd.Flags().SortFlags = false
|
||||
helpCmd := addCmd(rc, rootCmd, newHelpCmd())
|
||||
rootCmd.SetHelpCommand(helpCmd)
|
||||
@ -552,29 +553,30 @@ func newWriters(log lg.Log, cmd *cobra.Command, defaults config.Defaults, out, e
|
||||
var fm *output.Formatting
|
||||
fm, out2, errOut2 = getWriterFormatting(cmd, out, errOut)
|
||||
|
||||
// we need to determine --header here because the writer/format
|
||||
// constructor functions, e.g. table.NewRecordWriter, require it.
|
||||
printHeader := defaults.Header
|
||||
if cmdFlagChanged(cmd, flagHeader) {
|
||||
printHeader, _ = cmd.Flags().GetBool(flagHeader)
|
||||
}
|
||||
// FIXME: delete this section
|
||||
//// we need to determine --header here because the writer/format
|
||||
//// constructor functions, e.g. table.NewRecordWriter, require it.
|
||||
//printHeader := defaults.Header
|
||||
//if cmdFlagChanged(cmd, flagHeader) {
|
||||
// printHeader, _ = cmd.Flags().GetBool(flagHeader)
|
||||
//}
|
||||
//
|
||||
//verbose := false
|
||||
//if cmdFlagChanged(cmd, flagVerbose) {
|
||||
// verbose, _ = cmd.Flags().GetBool(flagVerbose)
|
||||
//}
|
||||
|
||||
verbose := false
|
||||
if cmdFlagChanged(cmd, flagVerbose) {
|
||||
verbose, _ = cmd.Flags().GetBool(flagVerbose)
|
||||
}
|
||||
|
||||
// Package tablew has writer impls for all of the writer interfaces,
|
||||
// Package tablew has writer impls for each of the writer interfaces,
|
||||
// so we use its writers as the baseline. Later we check the format
|
||||
// flags and set the various writer fields depending upon which
|
||||
// writers the format implements.
|
||||
w = &writers{
|
||||
fmt: fm,
|
||||
recordw: tablew.NewRecordWriter(out2, fm, printHeader),
|
||||
recordw: tablew.NewRecordWriter(out2, fm),
|
||||
metaw: tablew.NewMetadataWriter(out2, fm),
|
||||
srcw: tablew.NewSourceWriter(out2, fm, printHeader, verbose),
|
||||
srcw: tablew.NewSourceWriter(out2, fm),
|
||||
pingw: tablew.NewPingWriter(out2, fm),
|
||||
notifyw: tablew.NewNotifyWriter(out2, fm, printHeader),
|
||||
notifyw: tablew.NewNotifyWriter(out2, fm),
|
||||
errw: tablew.NewErrorWriter(errOut2, fm),
|
||||
}
|
||||
|
||||
@ -593,18 +595,18 @@ func newWriters(log lg.Log, cmd *cobra.Command, defaults config.Defaults, out, e
|
||||
// Table is the base format, already set above, no need to do anything.
|
||||
|
||||
case config.FormatTSV:
|
||||
w.recordw = csvw.NewRecordWriter(out2, printHeader, csvw.Tab)
|
||||
w.recordw = csvw.NewRecordWriter(out2, fm.ShowHeader, csvw.Tab)
|
||||
w.pingw = csvw.NewPingWriter(out2, csvw.Tab)
|
||||
|
||||
case config.FormatCSV:
|
||||
w.recordw = csvw.NewRecordWriter(out2, printHeader, csvw.Comma)
|
||||
w.recordw = csvw.NewRecordWriter(out2, fm.ShowHeader, csvw.Comma)
|
||||
w.pingw = csvw.NewPingWriter(out2, csvw.Comma)
|
||||
|
||||
case config.FormatXML:
|
||||
w.recordw = xmlw.NewRecordWriter(out2, fm)
|
||||
|
||||
case config.FormatXLSX:
|
||||
w.recordw = xlsxw.NewRecordWriter(out2, printHeader)
|
||||
w.recordw = xlsxw.NewRecordWriter(out2, fm.ShowHeader)
|
||||
|
||||
case config.FormatRaw:
|
||||
w.recordw = raww.NewRecordWriter(out2)
|
||||
@ -635,6 +637,14 @@ func getWriterFormatting(cmd *cobra.Command, out, errOut io.Writer) (fm *output.
|
||||
fm.Pretty, _ = cmd.Flags().GetBool(flagPretty)
|
||||
}
|
||||
|
||||
if cmdFlagChanged(cmd, flagVerbose) {
|
||||
fm.Verbose, _ = cmd.Flags().GetBool(flagVerbose)
|
||||
}
|
||||
|
||||
if cmdFlagChanged(cmd, flagHeader) {
|
||||
fm.ShowHeader, _ = cmd.Flags().GetBool(flagHeader)
|
||||
}
|
||||
|
||||
// TODO: Should get this default value from config
|
||||
colorize := true
|
||||
|
||||
|
@ -17,7 +17,8 @@ func newInspectCmd() *cobra.Command {
|
||||
RunE: execInspect,
|
||||
Short: "Inspect data source schema and stats",
|
||||
Long: `Inspect a data source, or a particular table in a source,
|
||||
listing table details, column names and types, row counts, etc.
|
||||
listing table details such as column names and row counts, etc. Use
|
||||
the --verbose flag to see more detail.
|
||||
If @HANDLE is not provided, the active data source is assumed.`,
|
||||
Example: ` # inspect active data source
|
||||
$ sq inspect
|
||||
@ -25,6 +26,9 @@ If @HANDLE is not provided, the active data source is assumed.`,
|
||||
# inspect @pg1 data source
|
||||
$ sq inspect @pg1
|
||||
|
||||
# inspect @pg1 data source, showing verbose output
|
||||
$ sq inspect -v @pg1
|
||||
|
||||
# inspect 'actor' in @pg1 data source
|
||||
$ sq inspect @pg1.actor
|
||||
|
||||
|
@ -12,7 +12,6 @@ func newSrcListCmd() *cobra.Command {
|
||||
RunE: execSrcList,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolP(flagVerbose, flagVerboseShort, false, flagVerboseUsage)
|
||||
cmd.Flags().BoolP(flagHeader, flagHeaderShort, false, flagHeaderUsage)
|
||||
return cmd
|
||||
}
|
||||
|
@ -85,6 +85,6 @@ More at https://sq.io
|
||||
addQueryCmdFlags(cmd)
|
||||
cmd.Flags().Bool(flagVersion, false, flagVersionUsage)
|
||||
cmd.PersistentFlags().BoolP(flagMonochrome, flagMonochromeShort, false, flagMonochromeUsage)
|
||||
|
||||
cmd.PersistentFlags().BoolP(flagVerbose, flagVerboseShort, false, flagVerboseUsage)
|
||||
return cmd
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ const (
|
||||
|
||||
flagVerbose = "verbose"
|
||||
flagVerboseShort = "v"
|
||||
flagVerboseUsage = "Print verbose data, if applicable"
|
||||
flagVerboseUsage = "Print verbose output, if applicable"
|
||||
|
||||
flagVersion = "version"
|
||||
flagVersionUsage = "Print sq version"
|
||||
|
@ -6,6 +6,14 @@ import "github.com/fatih/color"
|
||||
type Formatting struct {
|
||||
monochrome bool
|
||||
|
||||
// ShowHeader indicates that a header (e.g. a header row) should
|
||||
// be printed where applicable.
|
||||
ShowHeader bool
|
||||
|
||||
// Verbose indicates that verbose output should be printed where
|
||||
// applicable.
|
||||
Verbose bool
|
||||
|
||||
// Pretty indicates that output should be pretty-printed.
|
||||
// Typically this means indentation, new lines, etc, but
|
||||
// varies by output format.
|
||||
@ -67,6 +75,8 @@ type Formatting struct {
|
||||
// are enabled. The default indent is two spaces.
|
||||
func NewFormatting() *Formatting {
|
||||
fm := &Formatting{
|
||||
ShowHeader: true,
|
||||
Verbose: false,
|
||||
Pretty: true,
|
||||
monochrome: false,
|
||||
Indent: " ",
|
||||
|
@ -42,8 +42,7 @@ func (w *mdWriter) DriverMetadata(drvrs []driver.Metadata) error {
|
||||
|
||||
// TableMetadata implements output.MetadataWriter.
|
||||
func (w *mdWriter) TableMetadata(tblMeta *source.TableMetadata) error {
|
||||
headers := []string{"TABLE", "ROWS", "TYPE", "SIZE", "NUM COLS", "COL NAMES", "COL TYPES"}
|
||||
|
||||
var headers []string
|
||||
var rows [][]string
|
||||
|
||||
colNames := make([]string, len(tblMeta.Columns))
|
||||
@ -59,22 +58,39 @@ func (w *mdWriter) TableMetadata(tblMeta *source.TableMetadata) error {
|
||||
size = stringz.ByteSized(*tblMeta.Size, 1, "")
|
||||
}
|
||||
|
||||
row := []string{
|
||||
tblMeta.Name,
|
||||
fmt.Sprintf("%d", tblMeta.RowCount),
|
||||
tblMeta.TableType,
|
||||
size,
|
||||
fmt.Sprintf("%d", len(tblMeta.Columns)),
|
||||
strings.Join(colNames, ", "),
|
||||
strings.Join(colTypes, ", "),
|
||||
}
|
||||
rows = append(rows, row)
|
||||
if w.tbl.fm.Verbose {
|
||||
headers = []string{"TABLE", "ROWS", "TYPE", "SIZE", "NUM COLS", "COL NAMES", "COL TYPES"}
|
||||
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(3, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(4, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(3, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(4, w.tbl.fm.Number.SprintFunc())
|
||||
|
||||
row := []string{
|
||||
tblMeta.Name,
|
||||
fmt.Sprintf("%d", tblMeta.RowCount),
|
||||
tblMeta.TableType,
|
||||
size,
|
||||
fmt.Sprintf("%d", len(tblMeta.Columns)),
|
||||
strings.Join(colNames, ", "),
|
||||
strings.Join(colTypes, ", "),
|
||||
}
|
||||
rows = append(rows, row)
|
||||
} else {
|
||||
headers = []string{"TABLE", "ROWS", "COL NAMES"}
|
||||
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
|
||||
row := []string{
|
||||
tblMeta.Name,
|
||||
fmt.Sprintf("%d", tblMeta.RowCount),
|
||||
strings.Join(colNames, ", "),
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
w.tbl.appendRowsAndRenderAll(rows)
|
||||
return nil
|
||||
@ -119,7 +135,21 @@ func (w *mdWriter) SourceMetadata(meta *source.Metadata) error {
|
||||
w.tbl.reset()
|
||||
fmt.Fprintln(w.tbl.out)
|
||||
|
||||
headers = []string{"TABLE", "ROWS", "TYPE", "SIZE", "NUM COLS", "COL NAMES", "COL TYPES"}
|
||||
|
||||
if w.tbl.fm.Verbose {
|
||||
headers = []string{"TABLE", "ROWS", "TYPE", "SIZE", "NUM COLS", "COL NAMES", "COL TYPES"}
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(3, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(4, w.tbl.fm.Number.SprintFunc())
|
||||
|
||||
} else {
|
||||
headers = []string{"TABLE", "ROWS", "COL NAMES"}
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
}
|
||||
|
||||
var rows [][]string
|
||||
|
||||
@ -137,24 +167,27 @@ func (w *mdWriter) SourceMetadata(meta *source.Metadata) error {
|
||||
size = stringz.ByteSized(*tbl.Size, 1, "")
|
||||
}
|
||||
|
||||
row = []string{
|
||||
tbl.Name,
|
||||
fmt.Sprintf("%d", tbl.RowCount),
|
||||
tbl.TableType,
|
||||
size,
|
||||
fmt.Sprintf("%d", len(tbl.Columns)),
|
||||
strings.Join(colNames, ", "),
|
||||
strings.Join(colTypes, ", "),
|
||||
if w.tbl.fm.Verbose {
|
||||
row = []string{
|
||||
tbl.Name,
|
||||
fmt.Sprintf("%d", tbl.RowCount),
|
||||
tbl.TableType,
|
||||
size,
|
||||
fmt.Sprintf("%d", len(tbl.Columns)),
|
||||
strings.Join(colNames, ", "),
|
||||
strings.Join(colTypes, ", "),
|
||||
}
|
||||
} else {
|
||||
row = []string{
|
||||
tbl.Name,
|
||||
fmt.Sprintf("%d", tbl.RowCount),
|
||||
strings.Join(colNames, ", "),
|
||||
}
|
||||
}
|
||||
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
w.tbl.tblImpl.SetHeader(headers)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(1, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(3, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetColTrans(4, w.tbl.fm.Number.SprintFunc())
|
||||
|
||||
w.tbl.appendRowsAndRenderAll(rows)
|
||||
return nil
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ type notifyWriter struct {
|
||||
}
|
||||
|
||||
// NewNotifyWriter implements output.NotificationWriter.
|
||||
func NewNotifyWriter(out io.Writer, fm *output.Formatting, header bool) output.NotificationWriter {
|
||||
tbl := &table{out: out, header: header, fm: fm}
|
||||
func NewNotifyWriter(out io.Writer, fm *output.Formatting) output.NotificationWriter {
|
||||
tbl := &table{out: out, header: fm.ShowHeader, fm: fm }
|
||||
w := ¬ifyWriter{tbl: tbl}
|
||||
w.tbl.reset()
|
||||
return w
|
||||
|
@ -14,8 +14,8 @@ type recordWriter struct {
|
||||
}
|
||||
|
||||
// NewRecordWriter returns a RecordWriter for text table output.
|
||||
func NewRecordWriter(out io.Writer, fm *output.Formatting, header bool) output.RecordWriter {
|
||||
tbl := &table{out: out, fm: fm, header: header}
|
||||
func NewRecordWriter(out io.Writer, fm *output.Formatting) output.RecordWriter {
|
||||
tbl := &table{out: out, fm: fm, header: fm.ShowHeader}
|
||||
w := &recordWriter{tbl: tbl}
|
||||
w.tbl.reset()
|
||||
return w
|
||||
|
@ -16,9 +16,9 @@ type sourceWriter struct {
|
||||
|
||||
// NewSourceWriter returns a source writer that outputs source
|
||||
// details in text table format.
|
||||
func NewSourceWriter(out io.Writer, fm *output.Formatting, header, verbose bool) output.SourceWriter {
|
||||
tbl := &table{out: out, fm: fm, header: header}
|
||||
w := &sourceWriter{tbl: tbl, verbose: verbose}
|
||||
func NewSourceWriter(out io.Writer, fm *output.Formatting) output.SourceWriter {
|
||||
tbl := &table{out: out, fm: fm, header: fm.ShowHeader}
|
||||
w := &sourceWriter{tbl: tbl, verbose: fm.Verbose}
|
||||
w.tbl.reset()
|
||||
return w
|
||||
}
|
||||
@ -77,8 +77,8 @@ func (w *sourceWriter) SourceSet(ss *source.Set) error {
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
w.tbl.tblImpl.SetHeaderDisable(!w.tbl.header)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Number.SprintFunc())
|
||||
w.tbl.tblImpl.SetHeaderDisable(!w.tbl.fm.ShowHeader)
|
||||
w.tbl.tblImpl.SetColTrans(0, w.tbl.fm.Handle.SprintFunc())
|
||||
w.tbl.tblImpl.SetHeader([]string{"HANDLE", "DRIVER", "LOCATION", "OPTIONS"})
|
||||
w.tbl.appendRowsAndRenderAll(rows)
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user