这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions internal/constants/metaquery_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ const (
//CmdTableList = ".tables" // List all tables
CmdOutput = ".output" // Set output mode
//CmdTiming = ".timing" // Toggle query timer
CmdHeaders = ".header" // Toggle headers output
CmdSeparator = ".separator" // Set the column separator
CmdExit = ".exit" // Exit the interactive prompt
CmdQuit = ".quit" // Alias for .exit
//CmdInspect = ".inspect" // inspect
CmdHeaders = ".header" // Toggle headers output
CmdSeparator = ".separator" // Set the column separator
CmdExit = ".exit" // Exit the interactive prompt
CmdQuit = ".quit" // Alias for .exit
CmdInspect = ".inspect" // inspect
CmdMulti = ".multi" // toggle multi line query
CmdClear = ".clear" // clear the console
CmdHelp = ".help" // list all meta commands
Expand Down
39 changes: 39 additions & 0 deletions internal/database/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,42 @@ func GetTableViews(ctx context.Context) ([]string, error) {
}
return tableViews, nil
}

func GetTableViewSchema(ctx context.Context, viewName string) (map[string]string, error) {
// Open a DuckDB connection
db, err := sql.Open("duckdb", filepaths.TailpipeDbFilePath())
if err != nil {
return nil, fmt.Errorf("failed to open DuckDB connection: %w", err)
}
defer db.Close()

query := `
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = ? ORDER BY columns.column_name;
`
rows, err := db.QueryContext(ctx, query, viewName)
if err != nil {
return nil, fmt.Errorf("failed to get view schema for %s: %w", viewName, err)
}
defer rows.Close()

schema := make(map[string]string)
for rows.Next() {
var columnName, columnType string
err = rows.Scan(&columnName, &columnType)
if err != nil {
return nil, fmt.Errorf("failed to scan column schema: %w", err)
}
if strings.HasPrefix(columnType, "STRUCT") {
columnType = "STRUCT"
}
schema[columnName] = columnType
}

if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over view schema rows: %w", err)
}

return schema, nil
}
4 changes: 2 additions & 2 deletions internal/interactive/interactive_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,8 @@ func (c *InteractiveClient) queryCompleter(d prompt.Document) []prompt.Suggest {
s = append(s, suggestions...)
case metaquery.IsMetaQuery(text):
suggestions := metaquery.Complete(&metaquery.CompleterInput{
Query: text,
TableSuggestions: c.getTableSuggestions(lastWord(text)),
Query: text,
ViewSuggestions: c.getTableSuggestions(lastWord(text)),
})
s = append(s, suggestions...)
default:
Expand Down
10 changes: 5 additions & 5 deletions internal/metaquery/completers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

// CompleterInput is a struct defining input data for the metaquery completer
type CompleterInput struct {
Query string
TableSuggestions []prompt.Suggest
Query string
ViewSuggestions []prompt.Suggest
}

type completer func(input *CompleterInput) []prompt.Suggest
Expand Down Expand Up @@ -40,6 +40,6 @@ func completerFromArgsOf(cmd string) completer {
}
}

//func inspectCompleter(input *CompleterInput) []prompt.Suggest {
// return input.TableSuggestions
//}
func inspectCompleter(input *CompleterInput) []prompt.Suggest {
return input.ViewSuggestions
}
18 changes: 7 additions & 11 deletions internal/metaquery/definitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,13 @@ func init() {
},
completer: completerFromArgsOf(constants.CmdOutput),
},
//
//constants.CmdInspect: {
// title: constants.CmdInspect,
// handler: inspect,
// // .inspect only supports a single arg, however the arg validation code cannot understand escaped arguments
// // e.g. it will treat csv."my table" as 2 args
// // the logic to handle this escaping is lower down so we just validate to ensure at least one argument has been provided
// validator: atLeastNArgs(0),
// description: "View connections, tables & column information",
// completer: inspectCompleter,
//},
constants.CmdInspect: {
title: constants.CmdInspect,
handler: inspect,
validator: atMostNArgs(1),
description: "View tables & column information",
completer: inspectCompleter,
},
constants.CmdClear: {
title: constants.CmdClear,
handler: clearScreen,
Expand Down
17 changes: 17 additions & 0 deletions internal/metaquery/handler_input.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
package metaquery

import (
"context"

"github.com/c-bata/go-prompt"

"github.com/turbot/tailpipe/internal/database"
)

// HandlerInput defines input data for the metaquery handler
type HandlerInput struct {
Prompt *prompt.Prompt
ClosePrompt func()
Query string

views *[]string
}

func (h *HandlerInput) args() []string {
return getArguments(h.Query)
}

func (h *HandlerInput) GetViews() ([]string, error) {
if h.views == nil {
views, err := database.GetTableViews(context.Background())
if err != nil {
return nil, err
}
h.views = &views
}
return *h.views, nil
}
96 changes: 85 additions & 11 deletions internal/metaquery/handler_inspect.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,87 @@
package metaquery

//// inspect
//func inspect(ctx context.Context, input *HandlerInput) error {
// // TODO #metaquery - implement inspect
// return nil
//}
//
//// list all the tables in the schema
//func listTables(ctx context.Context, input *HandlerInput) error {
// // TODO #metaquery - implement listTables
// return nil
//}
import (
"context"
"fmt"
"slices"
"sort"
"strings"

"github.com/turbot/tailpipe/internal/config"
"github.com/turbot/tailpipe/internal/database"
"github.com/turbot/tailpipe/internal/plugin"
)

// inspect
func inspect(ctx context.Context, input *HandlerInput) error {

views, err := input.GetViews()
if err != nil {
return fmt.Errorf("failed to get tables: %w", err)
}

if len(input.args()) == 0 {
return listViews(ctx, input, views)
}

viewName := input.args()[0]
if slices.Contains(views, viewName) {
return listViewSchema(ctx, input, viewName)
}

return nil
}

func listViews(ctx context.Context, input *HandlerInput, views []string) error {
var rows [][]string
rows = append(rows, []string{"Table", "Plugin"}) // Header

for _, view := range views {
p, _ := getPluginForTable(ctx, view)
rows = append(rows, []string{view, p})
}

fmt.Println(buildTable(rows, true)) //nolint:forbidigo //UI output
return nil
}

func listViewSchema(ctx context.Context, input *HandlerInput, viewName string) error {
schema, err := database.GetTableViewSchema(ctx, viewName)
if err != nil {
return fmt.Errorf("failed to get view schema: %w", err)
}

var rows [][]string
rows = append(rows, []string{"Column", "Type"}) // Header

var cols []string
for column := range schema {
cols = append(cols, column)
}
sort.Strings(cols)

for _, col := range cols {
rows = append(rows, []string{col, schema[col]})
}

fmt.Println(buildTable(rows, true)) //nolint:forbidigo //UI output
return nil
}

func getPluginForTable(ctx context.Context, tableName string) (string, error) {
prefix := strings.Split(tableName, "_")[0]

ps, err := plugin.GetInstalledPlugins(ctx, config.GlobalConfig.PluginVersions)
if err != nil {
return "", fmt.Errorf("failed to get installed plugins: %w", err)
}

for k, v := range ps {
pluginShortName := strings.Split(k, "/")[1]
if strings.HasPrefix(pluginShortName, prefix) {
return fmt.Sprintf("%s@%s", pluginShortName, v.String()), nil
}
}

return "", nil
}
22 changes: 11 additions & 11 deletions internal/metaquery/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,17 @@ func validatorFromArgsOf(cmd string) validator {
// }
//}

//var atMostNArgs = func(n int) validator {
// return func(args []string) ValidationResult {
// numArgs := len(args)
// if numArgs > n {
// return ValidationResult{
// Err: fmt.Errorf("command needs at most %d %s - got %d", n, utils.Pluralize("argument", n), numArgs),
// }
// }
// return ValidationResult{ShouldRun: true}
// }
//}
var atMostNArgs = func(n int) validator {
return func(args []string) ValidationResult {
numArgs := len(args)
if numArgs > n {
return ValidationResult{
Err: fmt.Errorf("command needs at most %d %s - got %d", n, utils.Pluralize("argument", n), numArgs),
}
}
return ValidationResult{ShouldRun: true}
}
}

var exactlyNArgs = func(n int) validator {
return func(args []string) ValidationResult {
Expand Down
Loading