Skip to content

Commit 3715ae6

Browse files
committed
Support stdin in analyze and align parse output to a single document
Add stdin support to `sqlc analyze`: when no query file argument is given, the query is read from stdin (written to a temporary file so the compiler can read it), mirroring `sqlc parse`. Align the two commands on a single-document JSON output. `parse` previously emitted one JSON object per statement (newline-delimited), which is not parseable as a single document; it now emits a single JSON array of statements, matching the array `analyze` already produces.
1 parent d8c512f commit 3715ae6

2 files changed

Lines changed: 48 additions & 9 deletions

File tree

internal/cmd/analyze.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package cmd
33
import (
44
"encoding/json"
55
"fmt"
6+
"io"
7+
"os"
68
"strings"
79

810
"github.com/spf13/cobra"
@@ -31,8 +33,12 @@ Examples:
3133
sqlc analyze --dialect mysql --schema schema.sql query.sql
3234
3335
# Analyze a SQLite query
34-
sqlc analyze --dialect sqlite --schema schema.sql query.sql`,
35-
Args: cobra.ExactArgs(1),
36+
sqlc analyze --dialect sqlite --schema schema.sql query.sql
37+
38+
# Analyze a query piped via stdin
39+
echo "-- name: GetAuthor :one
40+
SELECT * FROM authors WHERE id = $1;" | sqlc analyze --dialect postgresql --schema schema.sql`,
41+
Args: cobra.MaximumNArgs(1),
3642
RunE: func(cmd *cobra.Command, args []string) error {
3743
dialect, err := cmd.Flags().GetString("dialect")
3844
if err != nil {
@@ -50,7 +56,38 @@ Examples:
5056
return fmt.Errorf("--schema flag is required")
5157
}
5258

53-
queryPath := args[0]
59+
// The query comes from a file argument or, when none is given, from
60+
// stdin. The compiler reads queries from files, so stdin is written to
61+
// a temporary file.
62+
var queryPath string
63+
if len(args) == 1 {
64+
queryPath = args[0]
65+
} else {
66+
stat, err := os.Stdin.Stat()
67+
if err != nil {
68+
return fmt.Errorf("failed to stat stdin: %w", err)
69+
}
70+
if (stat.Mode() & os.ModeCharDevice) != 0 {
71+
return fmt.Errorf("no query provided. Specify a query file or pipe SQL via stdin")
72+
}
73+
data, err := io.ReadAll(cmd.InOrStdin())
74+
if err != nil {
75+
return fmt.Errorf("failed to read stdin: %w", err)
76+
}
77+
tmp, err := os.CreateTemp("", "sqlc-analyze-*.sql")
78+
if err != nil {
79+
return fmt.Errorf("failed to create temp file: %w", err)
80+
}
81+
defer os.Remove(tmp.Name())
82+
if _, err := tmp.Write(data); err != nil {
83+
tmp.Close()
84+
return fmt.Errorf("failed to write temp file: %w", err)
85+
}
86+
if err := tmp.Close(); err != nil {
87+
return fmt.Errorf("failed to close temp file: %w", err)
88+
}
89+
queryPath = tmp.Name()
90+
}
5491

5592
var engine config.Engine
5693
switch dialect {

internal/cmd/parse.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,17 @@ Examples:
8585
return fmt.Errorf("parse error: %w", err)
8686
}
8787

88-
// Output AST as JSON
88+
// Output the AST as a single JSON document
89+
raws := make([]*ast.RawStmt, 0, len(stmts))
90+
for _, stmt := range stmts {
91+
raws = append(raws, stmt.Raw)
92+
}
93+
8994
stdout := cmd.OutOrStdout()
9095
encoder := json.NewEncoder(stdout)
9196
encoder.SetIndent("", " ")
92-
93-
for _, stmt := range stmts {
94-
if err := encoder.Encode(stmt.Raw); err != nil {
95-
return fmt.Errorf("failed to encode AST: %w", err)
96-
}
97+
if err := encoder.Encode(raws); err != nil {
98+
return fmt.Errorf("failed to encode AST: %w", err)
9799
}
98100

99101
return nil

0 commit comments

Comments
 (0)