mirror of
https://github.com/turbot/steampipe.git
synced 2026-02-22 05:00:21 -05:00
@@ -4,13 +4,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/display"
|
||||
"github.com/turbot/steampipe/statefile"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/turbot/steampipe-plugin-sdk/logging"
|
||||
"github.com/turbot/steampipe/cmdconfig"
|
||||
@@ -418,14 +417,12 @@ func runPluginListCmd(cmd *cobra.Command, args []string) {
|
||||
utils.ShowErrorWithMessage(err,
|
||||
fmt.Sprintf("Plugin Listing failed"))
|
||||
}
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleLight)
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.AppendHeader(table.Row{"Name", "Version", "Connections"})
|
||||
headers := []string{"Name", "Version", "Connections"}
|
||||
rows := [][]string{}
|
||||
for _, item := range list {
|
||||
t.AppendRow(table.Row{item.Name, item.Version, strings.Join(item.Connections, ",")})
|
||||
rows = append(rows, []string{item.Name, item.Version, strings.Join(item.Connections, ",")})
|
||||
}
|
||||
t.Render()
|
||||
display.ShowWrappedTable(headers, rows, false)
|
||||
}
|
||||
|
||||
func runPluginUninstallCmd(cmd *cobra.Command, args []string) {
|
||||
|
||||
@@ -10,17 +10,18 @@ import (
|
||||
|
||||
"github.com/turbot/steampipe/cmdconfig"
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/definitions/results"
|
||||
"github.com/turbot/steampipe/utils"
|
||||
)
|
||||
|
||||
// ExecuteSync :: execute a query against this client and wait for the result
|
||||
func (c *Client) ExecuteSync(query string) (*SyncQueryResult, error) {
|
||||
func (c *Client) ExecuteSync(query string) (*results.SyncQueryResult, error) {
|
||||
// https://github.com/golang/go/wiki/CodeReviewComments#indent-error-flow
|
||||
result, err := c.executeQuery(query, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
syncResult := &SyncQueryResult{ColTypes: result.ColTypes}
|
||||
syncResult := &results.SyncQueryResult{ColTypes: result.ColTypes}
|
||||
for row := range *result.RowChan {
|
||||
syncResult.Rows = append(syncResult.Rows, row)
|
||||
}
|
||||
@@ -28,9 +29,9 @@ func (c *Client) ExecuteSync(query string) (*SyncQueryResult, error) {
|
||||
return syncResult, nil
|
||||
}
|
||||
|
||||
func (c *Client) executeQuery(query string, countStream bool) (*QueryResult, error) {
|
||||
func (c *Client) executeQuery(query string, countStream bool) (*results.QueryResult, error) {
|
||||
if query == "" {
|
||||
return &QueryResult{}, nil
|
||||
return &results.QueryResult{}, nil
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
@@ -65,7 +66,7 @@ func (c *Client) executeQuery(query string, countStream bool) (*QueryResult, err
|
||||
}
|
||||
cols, err := rows.Columns()
|
||||
|
||||
result := newQueryResult(colTypes)
|
||||
result := results.NewQueryResult(colTypes)
|
||||
|
||||
rowCount := 0
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/turbot/steampipe/autocomplete"
|
||||
"github.com/turbot/steampipe/cmdconfig"
|
||||
"github.com/turbot/steampipe/definitions/results"
|
||||
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/metaquery"
|
||||
@@ -40,7 +41,7 @@ func (c *InteractiveClient) close() {
|
||||
}
|
||||
|
||||
// InteractiveQuery :: start an interactive prompt and return
|
||||
func (c *InteractiveClient) InteractiveQuery(resultsStreamer *ResultStreamer, onCompleteCallback func()) {
|
||||
func (c *InteractiveClient) InteractiveQuery(resultsStreamer *results.ResultStreamer, onCompleteCallback func()) {
|
||||
defer func() {
|
||||
|
||||
onCompleteCallback()
|
||||
@@ -60,7 +61,7 @@ func (c *InteractiveClient) InteractiveQuery(resultsStreamer *ResultStreamer, on
|
||||
// this needs to be the last thing we do,
|
||||
// as the runQueryCmd uses this as an indication
|
||||
// to quit out of the application
|
||||
resultsStreamer.close()
|
||||
resultsStreamer.Close()
|
||||
}()
|
||||
|
||||
fmt.Printf("Welcome to Steampipe v%s\n", version.String())
|
||||
@@ -81,7 +82,7 @@ func (c *InteractiveClient) InteractiveQuery(resultsStreamer *ResultStreamer, on
|
||||
}
|
||||
}
|
||||
|
||||
func (c *InteractiveClient) runInteractivePrompt(resultsStreamer *ResultStreamer) (ret utils.InteractiveExitStatus) {
|
||||
func (c *InteractiveClient) runInteractivePrompt(resultsStreamer *results.ResultStreamer) (ret utils.InteractiveExitStatus) {
|
||||
defer func() {
|
||||
// this is to catch the PANIC that gets raised by
|
||||
// the executor of go-prompt
|
||||
@@ -171,7 +172,7 @@ func (c *InteractiveClient) breakMultilinePrompt(buffer *prompt.Buffer) {
|
||||
c.interactiveBuffer = []string{}
|
||||
}
|
||||
|
||||
func (c *InteractiveClient) executor(line string, resultsStreamer *ResultStreamer) {
|
||||
func (c *InteractiveClient) executor(line string, resultsStreamer *results.ResultStreamer) {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// if it's an empty line, then we don't need to do anything
|
||||
@@ -210,7 +211,7 @@ func (c *InteractiveClient) executor(line string, resultsStreamer *ResultStreame
|
||||
utils.ShowError(err)
|
||||
resultsStreamer.Done()
|
||||
} else {
|
||||
resultsStreamer.streamResult(result)
|
||||
resultsStreamer.StreamResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,12 @@ import (
|
||||
"github.com/turbot/steampipe-plugin-sdk/logging"
|
||||
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/definitions/results"
|
||||
"github.com/turbot/steampipe/utils"
|
||||
)
|
||||
|
||||
// ExecuteQuery :: entry point for executing ad-hoc queries from outside the package
|
||||
func ExecuteQuery(queryString string) (*ResultStreamer, error) {
|
||||
func ExecuteQuery(queryString string) (*results.ResultStreamer, error) {
|
||||
var err error
|
||||
|
||||
logging.LogTime("db.ExecuteQuery start")
|
||||
@@ -48,7 +49,7 @@ func ExecuteQuery(queryString string) (*ResultStreamer, error) {
|
||||
return nil, fmt.Errorf("failed to add functions: %v", err)
|
||||
}
|
||||
|
||||
resultsStreamer := newQueryResults()
|
||||
resultsStreamer := results.NewResultStreamer()
|
||||
|
||||
// this is a callback to close the db et-al. when things get done - no matter the mode
|
||||
onComplete := func() { Shutdown(client, InvokerQuery) }
|
||||
@@ -69,13 +70,15 @@ func ExecuteQuery(queryString string) (*ResultStreamer, error) {
|
||||
onComplete()
|
||||
return nil, err
|
||||
}
|
||||
go resultsStreamer.streamSingleResult(result, onComplete)
|
||||
go resultsStreamer.StreamSingleResult(result, onComplete)
|
||||
}
|
||||
|
||||
logging.LogTime("db.ExecuteQuery end")
|
||||
return resultsStreamer, nil
|
||||
}
|
||||
|
||||
// Shutdown :: closes the client connection and stops the
|
||||
// database instance if the given `invoker` matches
|
||||
func Shutdown(client *Client, invoker Invoker) {
|
||||
log.Println("[TRACE] shutdown")
|
||||
if client != nil {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
package db
|
||||
/**
|
||||
This package is for all interfaces that are imported in multiple packages in the
|
||||
code base
|
||||
|
||||
This package MUST never import any other `steampipe` package
|
||||
**/
|
||||
package results
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
@@ -29,7 +35,7 @@ func (r QueryResult) StreamError(err error) {
|
||||
*r.RowChan <- &RowResult{Error: err}
|
||||
}
|
||||
|
||||
func newQueryResult(colTypes []*sql.ColumnType) *QueryResult {
|
||||
func NewQueryResult(colTypes []*sql.ColumnType) *QueryResult {
|
||||
rowChan := make(chan *RowResult)
|
||||
return &QueryResult{
|
||||
RowChan: &rowChan,
|
||||
@@ -1,11 +1,11 @@
|
||||
package db
|
||||
package results
|
||||
|
||||
type ResultStreamer struct {
|
||||
Results chan *QueryResult
|
||||
displayReady chan string
|
||||
}
|
||||
|
||||
func newQueryResults() *ResultStreamer {
|
||||
func NewResultStreamer() *ResultStreamer {
|
||||
return &ResultStreamer{
|
||||
// make buffered channel so we can always stream a single result
|
||||
Results: make(chan *QueryResult, 1),
|
||||
@@ -13,18 +13,18 @@ func newQueryResults() *ResultStreamer {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *ResultStreamer) streamResult(result *QueryResult) {
|
||||
func (q *ResultStreamer) StreamResult(result *QueryResult) {
|
||||
q.Results <- result
|
||||
}
|
||||
|
||||
func (q *ResultStreamer) streamSingleResult(result *QueryResult, onComplete func()) {
|
||||
func (q *ResultStreamer) StreamSingleResult(result *QueryResult, onComplete func()) {
|
||||
q.Results <- result
|
||||
q.Wait()
|
||||
onComplete()
|
||||
close(q.Results)
|
||||
}
|
||||
|
||||
func (q *ResultStreamer) close() {
|
||||
func (q *ResultStreamer) Close() {
|
||||
close(q.Results)
|
||||
}
|
||||
|
||||
@@ -11,15 +11,16 @@ import (
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/jedib0t/go-pretty/v6/text"
|
||||
"github.com/karrick/gows"
|
||||
|
||||
"github.com/turbot/steampipe/cmdconfig"
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/db"
|
||||
"github.com/turbot/steampipe/definitions/results"
|
||||
"github.com/turbot/steampipe/utils"
|
||||
)
|
||||
|
||||
// ShowOutput :: displays the output using the proper formatter as applicable
|
||||
func ShowOutput(result *db.QueryResult) {
|
||||
func ShowOutput(result *results.QueryResult) {
|
||||
if cmdconfig.Viper().Get(constants.ArgOutput) == constants.ArgJSON {
|
||||
displayJSON(result)
|
||||
} else if cmdconfig.Viper().Get(constants.ArgOutput) == constants.ArgCSV {
|
||||
@@ -32,7 +33,74 @@ func ShowOutput(result *db.QueryResult) {
|
||||
}
|
||||
}
|
||||
|
||||
func displayLine(result *db.QueryResult) {
|
||||
func ShowWrappedTable(headers []string, rows [][]string, autoMerge bool) {
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleDefault)
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
|
||||
rowConfig := table.RowConfig{AutoMerge: autoMerge}
|
||||
colConfigs, headerRow := getColumnSettings(headers, rows)
|
||||
|
||||
t.SetColumnConfigs(colConfigs)
|
||||
t.AppendHeader(headerRow)
|
||||
|
||||
for _, row := range rows {
|
||||
rowObj := table.Row{}
|
||||
for _, col := range row {
|
||||
rowObj = append(rowObj, col)
|
||||
}
|
||||
t.AppendRow(rowObj, rowConfig)
|
||||
}
|
||||
t.Render()
|
||||
}
|
||||
|
||||
// calculate and returns column configuration based on header and row content
|
||||
func getColumnSettings(headers []string, rows [][]string) ([]table.ColumnConfig, table.Row) {
|
||||
maxCols, _, _ := gows.GetWinSize()
|
||||
colConfigs := make([]table.ColumnConfig, len(headers))
|
||||
headerRow := make(table.Row, len(headers))
|
||||
|
||||
sumOfAllCols := 0
|
||||
|
||||
// account for the spaces around the value of a column and separators
|
||||
spaceAccounting := ((len(headers) * 3) + 1)
|
||||
|
||||
for idx, colName := range headers {
|
||||
headerRow[idx] = colName
|
||||
|
||||
// get the maximum len of strings in this column
|
||||
maxLen := 0
|
||||
for _, row := range rows {
|
||||
colVal := row[idx]
|
||||
if len(colVal) > maxLen {
|
||||
maxLen = len(colVal)
|
||||
}
|
||||
if len(colName) > maxLen {
|
||||
maxLen = len(colName)
|
||||
}
|
||||
}
|
||||
colConfigs[idx] = table.ColumnConfig{
|
||||
Name: colName,
|
||||
Number: idx + 1,
|
||||
WidthMax: maxLen,
|
||||
WidthMin: maxLen,
|
||||
}
|
||||
sumOfAllCols += maxLen
|
||||
}
|
||||
|
||||
// now that all columns are set to the widths that they need,
|
||||
// set the last one to occupy as much as is available - no more - no less
|
||||
sumOfRest := sumOfAllCols - colConfigs[len(colConfigs)-1].WidthMax
|
||||
|
||||
if sumOfAllCols > maxCols {
|
||||
colConfigs[len(colConfigs)-1].WidthMax = (maxCols - sumOfRest - spaceAccounting)
|
||||
colConfigs[len(colConfigs)-1].WidthMin = (maxCols - sumOfRest - spaceAccounting)
|
||||
}
|
||||
|
||||
return colConfigs, headerRow
|
||||
}
|
||||
|
||||
func displayLine(result *results.QueryResult) {
|
||||
colNames := ColumnNames(result.ColTypes)
|
||||
maxColNameLength := 0
|
||||
for _, colName := range colNames {
|
||||
@@ -44,7 +112,7 @@ func displayLine(result *db.QueryResult) {
|
||||
itemIdx := 0
|
||||
|
||||
// define a function to display each row
|
||||
rowFunc := func(row []interface{}, result *db.QueryResult) {
|
||||
rowFunc := func(row []interface{}, result *results.QueryResult) {
|
||||
recordAsString, _ := ColumnValuesAsString(row, result.ColTypes)
|
||||
requiredTerminalColumnsForValuesOfRecord := 0
|
||||
for _, colValue := range recordAsString {
|
||||
@@ -98,11 +166,11 @@ func getTerminalColumnsRequiredForString(str string) int {
|
||||
return colsRequired
|
||||
}
|
||||
|
||||
func displayJSON(result *db.QueryResult) {
|
||||
func displayJSON(result *results.QueryResult) {
|
||||
var jsonOutput []map[string]interface{}
|
||||
|
||||
// define function to add each row to the JSON output
|
||||
rowFunc := func(row []interface{}, result *db.QueryResult) {
|
||||
rowFunc := func(row []interface{}, result *results.QueryResult) {
|
||||
record := map[string]interface{}{}
|
||||
for idx, colType := range result.ColTypes {
|
||||
value, _ := ParseJSONOutputColumnValue(row[idx], colType)
|
||||
@@ -125,7 +193,7 @@ func displayJSON(result *db.QueryResult) {
|
||||
fmt.Printf("%s\n", string(data))
|
||||
}
|
||||
|
||||
func displayCSV(result *db.QueryResult) {
|
||||
func displayCSV(result *results.QueryResult) {
|
||||
csvWriter := csv.NewWriter(os.Stdout)
|
||||
csvWriter.Comma = []rune(cmdconfig.Viper().GetString(constants.ArgSeparator))[0]
|
||||
|
||||
@@ -135,7 +203,7 @@ func displayCSV(result *db.QueryResult) {
|
||||
|
||||
// print the data as it comes
|
||||
// define function display each csv row
|
||||
rowFunc := func(row []interface{}, result *db.QueryResult) {
|
||||
rowFunc := func(row []interface{}, result *results.QueryResult) {
|
||||
rowAsString, _ := ColumnValuesAsString(row, result.ColTypes)
|
||||
_ = csvWriter.Write(rowAsString)
|
||||
}
|
||||
@@ -152,7 +220,7 @@ func displayCSV(result *db.QueryResult) {
|
||||
}
|
||||
}
|
||||
|
||||
func displayTable(result *db.QueryResult) {
|
||||
func displayTable(result *results.QueryResult) {
|
||||
// the buffer to put the output data in
|
||||
outbuf := bytes.NewBufferString("")
|
||||
|
||||
@@ -178,7 +246,7 @@ func displayTable(result *db.QueryResult) {
|
||||
t.AppendHeader(headers)
|
||||
|
||||
// define a function to execute for each row
|
||||
rowFunc := func(row []interface{}, result *db.QueryResult) {
|
||||
rowFunc := func(row []interface{}, result *results.QueryResult) {
|
||||
rowAsString, _ := ColumnValuesAsString(row, result.ColTypes)
|
||||
rowObj := table.Row{}
|
||||
for _, col := range rowAsString {
|
||||
@@ -205,10 +273,10 @@ func displayTable(result *db.QueryResult) {
|
||||
ShowPaged(outbuf.String())
|
||||
}
|
||||
|
||||
type displayResultsFunc func(row []interface{}, result *db.QueryResult)
|
||||
type displayResultsFunc func(row []interface{}, result *results.QueryResult)
|
||||
|
||||
// call func displayResult for each row of results
|
||||
func iterateResults(result *db.QueryResult, displayResult displayResultsFunc) error {
|
||||
func iterateResults(result *results.QueryResult, displayResult displayResultsFunc) error {
|
||||
for row := range *result.RowChan {
|
||||
if row == nil {
|
||||
return nil
|
||||
|
||||
@@ -2,19 +2,18 @@ package metaquery
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/c-bata/go-prompt"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"github.com/karrick/gows"
|
||||
"github.com/turbot/go-kit/helpers"
|
||||
typeHelpers "github.com/turbot/go-kit/types"
|
||||
"github.com/turbot/steampipe/cmdconfig"
|
||||
"github.com/turbot/steampipe/connection_config"
|
||||
"github.com/turbot/steampipe/constants"
|
||||
"github.com/turbot/steampipe/display"
|
||||
"github.com/turbot/steampipe/schema"
|
||||
"github.com/turbot/steampipe/utils"
|
||||
)
|
||||
@@ -174,7 +173,7 @@ To get information about the columns in a table, run '.inspect {connection}.{tab
|
||||
})
|
||||
rows = append(rows, tables...)
|
||||
}
|
||||
writeTable(header, rows, true)
|
||||
display.ShowWrappedTable(header, rows, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -233,7 +232,7 @@ func listConnections(input *HandlerInput) error {
|
||||
return rows[i][0] < rows[j][0]
|
||||
})
|
||||
|
||||
writeTable(header, rows, false)
|
||||
display.ShowWrappedTable(header, rows, false)
|
||||
|
||||
fmt.Printf(`
|
||||
To get information about the tables in a connection, run '.inspect {connection}'
|
||||
@@ -263,7 +262,7 @@ func inspectConnection(connectionName string, input *HandlerInput) bool {
|
||||
return rows[i][0] < rows[j][0]
|
||||
})
|
||||
|
||||
writeTable(header, rows, false)
|
||||
display.ShowWrappedTable(header, rows, false)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -295,78 +294,11 @@ func inspectTable(connectionName string, tableName string, input *HandlerInput)
|
||||
return rows[i][0] < rows[j][0]
|
||||
})
|
||||
|
||||
writeTable(header, rows, false)
|
||||
display.ShowWrappedTable(header, rows, false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeTable(headers []string, rows [][]string, autoMerge bool) {
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleDefault)
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
|
||||
rowConfig := table.RowConfig{AutoMerge: autoMerge}
|
||||
colConfigs, headerRow := getColumnSettings(headers, rows)
|
||||
|
||||
t.SetColumnConfigs(colConfigs)
|
||||
t.AppendHeader(headerRow)
|
||||
|
||||
for _, row := range rows {
|
||||
rowObj := table.Row{}
|
||||
for _, col := range row {
|
||||
rowObj = append(rowObj, col)
|
||||
}
|
||||
t.AppendRow(rowObj, rowConfig)
|
||||
}
|
||||
t.Render()
|
||||
}
|
||||
|
||||
// calculate and returns column configuration based on header and row content
|
||||
func getColumnSettings(headers []string, rows [][]string) ([]table.ColumnConfig, table.Row) {
|
||||
maxCols, _, _ := gows.GetWinSize()
|
||||
colConfigs := make([]table.ColumnConfig, len(headers))
|
||||
headerRow := make(table.Row, len(headers))
|
||||
|
||||
sumOfAllCols := 0
|
||||
|
||||
// account for the spaces around the value of a column and separators
|
||||
spaceAccounting := ((len(headers) * 3) + 1)
|
||||
|
||||
for idx, colName := range headers {
|
||||
headerRow[idx] = colName
|
||||
|
||||
// get the maximum len of strings in this column
|
||||
maxLen := 0
|
||||
for _, row := range rows {
|
||||
colVal := row[idx]
|
||||
if len(colVal) > maxLen {
|
||||
maxLen = len(colVal)
|
||||
}
|
||||
if len(colName) > maxLen {
|
||||
maxLen = len(colName)
|
||||
}
|
||||
}
|
||||
colConfigs[idx] = table.ColumnConfig{
|
||||
Name: colName,
|
||||
Number: idx + 1,
|
||||
WidthMax: maxLen,
|
||||
WidthMin: maxLen,
|
||||
}
|
||||
sumOfAllCols += maxLen
|
||||
}
|
||||
|
||||
// now that all columns are set to the widths that they need,
|
||||
// set the last one to occupy as much as is available - no more - no less
|
||||
sumOfRest := sumOfAllCols - colConfigs[len(colConfigs)-1].WidthMax
|
||||
|
||||
if sumOfAllCols > maxCols {
|
||||
colConfigs[len(colConfigs)-1].WidthMax = (maxCols - sumOfRest - spaceAccounting)
|
||||
colConfigs[len(colConfigs)-1].WidthMin = (maxCols - sumOfRest - spaceAccounting)
|
||||
}
|
||||
|
||||
return colConfigs, headerRow
|
||||
}
|
||||
|
||||
func buildTable(rows [][]string, autoMerge bool) string {
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleDefault)
|
||||
|
||||
Reference in New Issue
Block a user