Skip to content

Commit c8e6734

Browse files
committed
Command to run the queries
1 parent 79cffaf commit c8e6734

11 files changed

Lines changed: 466 additions & 43 deletions

File tree

cmd/query/archive.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package query
22

33
import (
44
"fmt"
5-
"strconv"
65

76
"github.com/duneanalytics/cli/cmdutil"
87
"github.com/duneanalytics/cli/output"
@@ -23,9 +22,9 @@ func newArchiveCmd() *cobra.Command {
2322
}
2423

2524
func runArchive(cmd *cobra.Command, args []string) error {
26-
queryID, err := strconv.Atoi(args[0])
25+
queryID, err := parseQueryID(args[0])
2726
if err != nil {
28-
return fmt.Errorf("invalid query ID %q: must be an integer", args[0])
27+
return err
2928
}
3029

3130
client := cmdutil.ClientFromCmd(cmd)

cmd/query/get.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package query
22

33
import (
44
"fmt"
5-
"strconv"
65
"strings"
76

87
"github.com/duneanalytics/cli/cmdutil"
@@ -24,9 +23,9 @@ func newGetCmd() *cobra.Command {
2423
}
2524

2625
func runGet(cmd *cobra.Command, args []string) error {
27-
queryID, err := strconv.Atoi(args[0])
26+
queryID, err := parseQueryID(args[0])
2827
if err != nil {
29-
return fmt.Errorf("invalid query ID %q: must be an integer", args[0])
28+
return err
3029
}
3130

3231
client := cmdutil.ClientFromCmd(cmd)

cmd/query/helpers.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package query
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
)
7+
8+
func parseQueryID(arg string) (int, error) {
9+
id, err := strconv.Atoi(arg)
10+
if err != nil {
11+
return 0, fmt.Errorf("invalid query ID %q: must be an integer", arg)
12+
}
13+
return id, nil
14+
}

cmd/query/query.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ func NewQueryCmd() *cobra.Command {
1212
cmd.AddCommand(newGetCmd())
1313
cmd.AddCommand(newUpdateCmd())
1414
cmd.AddCommand(newArchiveCmd())
15+
cmd.AddCommand(newRunCmd())
1516
return cmd
1617
}

cmd/query/run.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package query
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
"github.com/duneanalytics/cli/cmdutil"
9+
"github.com/duneanalytics/cli/output"
10+
"github.com/duneanalytics/duneapi-client-go/models"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
func newRunCmd() *cobra.Command {
15+
cmd := &cobra.Command{
16+
Use: "run <query-id>",
17+
Short: "Execute a saved query and display results",
18+
Args: cobra.ExactArgs(1),
19+
RunE: runRun,
20+
}
21+
22+
cmd.Flags().StringArray("param", nil, "query parameter in key=value format (repeatable)")
23+
cmd.Flags().String("performance", "medium", `performance tier: "medium" or "large"`)
24+
cmd.Flags().Int("limit", 0, "maximum number of rows to display (0 = all)")
25+
cmd.Flags().Bool("no-wait", false, "submit execution and exit without waiting for results")
26+
output.AddFormatFlag(cmd, "text")
27+
28+
return cmd
29+
}
30+
31+
func runRun(cmd *cobra.Command, args []string) error {
32+
queryID, err := parseQueryID(args[0])
33+
if err != nil {
34+
return err
35+
}
36+
37+
paramFlags, _ := cmd.Flags().GetStringArray("param")
38+
params, err := parseParams(paramFlags)
39+
if err != nil {
40+
return err
41+
}
42+
43+
performance, _ := cmd.Flags().GetString("performance")
44+
if performance != "medium" && performance != "large" {
45+
return fmt.Errorf("invalid performance tier %q: must be \"medium\" or \"large\"", performance)
46+
}
47+
48+
req := models.ExecuteRequest{
49+
QueryID: queryID,
50+
Performance: performance,
51+
}
52+
if len(params) > 0 {
53+
req.QueryParameters = params
54+
}
55+
56+
noWait, _ := cmd.Flags().GetBool("no-wait")
57+
if noWait {
58+
return runNoWait(cmd, req)
59+
}
60+
return runWait(cmd, req)
61+
}
62+
63+
func runNoWait(cmd *cobra.Command, req models.ExecuteRequest) error {
64+
client := cmdutil.ClientFromCmd(cmd)
65+
66+
resp, err := client.QueryExecute(req)
67+
if err != nil {
68+
return err
69+
}
70+
71+
w := cmd.OutOrStdout()
72+
switch output.FormatFromCmd(cmd) {
73+
case output.FormatJSON:
74+
return output.PrintJSON(w, resp)
75+
default:
76+
fmt.Fprintf(w, "Execution ID: %s\n", resp.ExecutionID)
77+
fmt.Fprintf(w, "State: %s\n", resp.State)
78+
return nil
79+
}
80+
}
81+
82+
func runWait(cmd *cobra.Command, req models.ExecuteRequest) error {
83+
client := cmdutil.ClientFromCmd(cmd)
84+
85+
exec, err := client.RunQuery(req)
86+
if err != nil {
87+
return err
88+
}
89+
90+
resp, err := exec.WaitGetResults(5*time.Second, 60)
91+
if err != nil {
92+
return err
93+
}
94+
95+
if resp.State != "QUERY_STATE_COMPLETED" {
96+
msg := fmt.Sprintf("query execution failed with state %s", resp.State)
97+
if resp.Error != nil {
98+
msg += fmt.Sprintf(": %s", resp.Error.Message)
99+
}
100+
return fmt.Errorf("%s", msg)
101+
}
102+
103+
return displayResults(cmd, resp)
104+
}
105+
106+
func parseParams(raw []string) (map[string]any, error) {
107+
if len(raw) == 0 {
108+
return nil, nil
109+
}
110+
params := make(map[string]any, len(raw))
111+
for _, s := range raw {
112+
key, value, ok := strings.Cut(s, "=")
113+
if !ok {
114+
return nil, fmt.Errorf("invalid parameter %q: expected key=value format", s)
115+
}
116+
if key == "" {
117+
return nil, fmt.Errorf("invalid parameter %q: key cannot be empty", s)
118+
}
119+
params[key] = value
120+
}
121+
return params, nil
122+
}
123+
124+
func displayResults(cmd *cobra.Command, resp *models.ResultsResponse) error {
125+
w := cmd.OutOrStdout()
126+
127+
if output.FormatFromCmd(cmd) == output.FormatJSON {
128+
return output.PrintJSON(w, resp)
129+
}
130+
131+
limit, _ := cmd.Flags().GetInt("limit")
132+
columns := resp.Result.Metadata.ColumnNames
133+
sourceRows := resp.Result.Rows
134+
totalRows := len(sourceRows)
135+
136+
if limit > 0 && limit < totalRows {
137+
sourceRows = sourceRows[:limit]
138+
}
139+
rows := resultRowsToStrings(sourceRows, columns)
140+
141+
output.PrintTable(w, columns, rows)
142+
if limit > 0 && limit < totalRows {
143+
fmt.Fprintf(w, "\nShowing %d of %d rows\n", limit, totalRows)
144+
} else {
145+
fmt.Fprintf(w, "\n%d rows\n", totalRows)
146+
}
147+
return nil
148+
}
149+
150+
func resultRowsToStrings(rows []map[string]any, columns []string) [][]string {
151+
out := make([][]string, len(rows))
152+
for i, row := range rows {
153+
cells := make([]string, len(columns))
154+
for j, col := range columns {
155+
cells[j] = fmt.Sprintf("%v", row[col])
156+
}
157+
out[i] = cells
158+
}
159+
return out
160+
}

0 commit comments

Comments
 (0)