@@ -16,10 +16,11 @@ import (
1616 "github.com/sqlc-dev/sqlc/internal/sql/ast"
1717)
1818
19- var analyzeCmd = & cobra.Command {
20- Use : "analyze [query-file]" ,
21- Short : "Analyze a query against a schema and output the result columns and parameters" ,
22- Long : `Analyze a query file against a schema file and output the inferred result
19+ func newAnalyzeCmd () * cobra.Command {
20+ cmd := & cobra.Command {
21+ Use : "analyze [query-file]" ,
22+ Short : "Analyze a query against a schema and output the result columns and parameters" ,
23+ Long : `Analyze a query file against a schema file and output the inferred result
2324columns and parameters as JSON.
2425
2526Unlike "sqlc generate", this command does not require a configuration file and
@@ -42,112 +43,117 @@ Examples:
4243
4344 # Include the statement AST in the output
4445 sqlc analyze --dialect postgresql --schema schema.sql --ast query.sql` ,
45- Args : cobra .MaximumNArgs (1 ),
46- RunE : func (cmd * cobra.Command , args []string ) error {
47- dialect , err := cmd .Flags ().GetString ("dialect" )
48- if err != nil {
49- return err
50- }
51- if dialect == "" {
52- return fmt .Errorf ("--dialect flag is required (postgresql, mysql, or sqlite)" )
53- }
54-
55- schemaPath , err := cmd .Flags ().GetString ("schema" )
56- if err != nil {
57- return err
58- }
59- if schemaPath == "" {
60- return fmt .Errorf ("--schema flag is required" )
61- }
62-
63- includeAST , err := cmd .Flags ().GetBool ("ast" )
64- if err != nil {
65- return err
66- }
67-
68- // The query comes from a file argument or, when none is given, from
69- // stdin. The compiler reads queries from files, so stdin is written to
70- // a temporary file.
71- var queryPath string
72- if len (args ) == 1 {
73- queryPath = args [0 ]
74- } else {
75- stat , err := os .Stdin .Stat ()
46+ Args : cobra .MaximumNArgs (1 ),
47+ RunE : func (cmd * cobra.Command , args []string ) error {
48+ dialect , err := cmd .Flags ().GetString ("dialect" )
7649 if err != nil {
77- return fmt . Errorf ( "failed to stat stdin: %w" , err )
50+ return err
7851 }
79- if ( stat . Mode () & os . ModeCharDevice ) != 0 {
80- return fmt .Errorf ("no query provided. Specify a query file or pipe SQL via stdin " )
52+ if dialect == "" {
53+ return fmt .Errorf ("--dialect flag is required (postgresql, mysql, or sqlite) " )
8154 }
82- data , err := io .ReadAll (cmd .InOrStdin ())
55+
56+ schemaPath , err := cmd .Flags ().GetString ("schema" )
57+ if err != nil {
58+ return err
59+ }
60+ if schemaPath == "" {
61+ return fmt .Errorf ("--schema flag is required" )
62+ }
63+
64+ includeAST , err := cmd .Flags ().GetBool ("ast" )
8365 if err != nil {
84- return fmt .Errorf ("failed to read stdin: %w" , err )
66+ return err
67+ }
68+
69+ // The query comes from a file argument or, when none is given, from
70+ // stdin. The compiler reads queries from files, so stdin is written to
71+ // a temporary file.
72+ var queryPath string
73+ if len (args ) == 1 {
74+ queryPath = args [0 ]
75+ } else {
76+ stat , err := os .Stdin .Stat ()
77+ if err != nil {
78+ return fmt .Errorf ("failed to stat stdin: %w" , err )
79+ }
80+ if (stat .Mode () & os .ModeCharDevice ) != 0 {
81+ return fmt .Errorf ("no query provided. Specify a query file or pipe SQL via stdin" )
82+ }
83+ data , err := io .ReadAll (cmd .InOrStdin ())
84+ if err != nil {
85+ return fmt .Errorf ("failed to read stdin: %w" , err )
86+ }
87+ tmp , err := os .CreateTemp ("" , "sqlc-analyze-*.sql" )
88+ if err != nil {
89+ return fmt .Errorf ("failed to create temp file: %w" , err )
90+ }
91+ defer os .Remove (tmp .Name ())
92+ if _ , err := tmp .Write (data ); err != nil {
93+ tmp .Close ()
94+ return fmt .Errorf ("failed to write temp file: %w" , err )
95+ }
96+ if err := tmp .Close (); err != nil {
97+ return fmt .Errorf ("failed to close temp file: %w" , err )
98+ }
99+ queryPath = tmp .Name ()
100+ }
101+
102+ var engine config.Engine
103+ switch dialect {
104+ case "postgresql" , "postgres" , "pg" :
105+ engine = config .EnginePostgreSQL
106+ case "mysql" :
107+ engine = config .EngineMySQL
108+ case "sqlite" :
109+ engine = config .EngineSQLite
110+ default :
111+ return fmt .Errorf ("unsupported dialect: %s (use postgresql, mysql, or sqlite)" , dialect )
112+ }
113+
114+ sql := config.SQL {
115+ Engine : engine ,
116+ Schema : config.Paths {schemaPath },
117+ Queries : config.Paths {queryPath },
85118 }
86- tmp , err := os .CreateTemp ("" , "sqlc-analyze-*.sql" )
119+ combo := config .Combine (config.Config {}, sql )
120+ parserOpts := opts.Parser {}
121+
122+ ctx := cmd .Context ()
123+ c , err := compiler .NewCompiler (sql , combo , parserOpts )
87124 if err != nil {
88- return fmt .Errorf ("failed to create temp file: %w" , err )
125+ return fmt .Errorf ("error creating compiler: %w" , err )
126+ }
127+ defer c .Close (ctx )
128+
129+ if err := c .ParseCatalog (sql .Schema ); err != nil {
130+ return fmt .Errorf ("error parsing schema: %w" , formatParseError (err ))
131+ }
132+ if err := c .ParseQueries (sql .Queries , parserOpts ); err != nil {
133+ return fmt .Errorf ("error parsing queries: %w" , formatParseError (err ))
89134 }
90- defer os .Remove (tmp .Name ())
91- if _ , err := tmp .Write (data ); err != nil {
92- tmp .Close ()
93- return fmt .Errorf ("failed to write temp file: %w" , err )
135+
136+ result := c .Result ()
137+
138+ out := make ([]analyzedQuery , 0 , len (result .Queries ))
139+ for _ , q := range result .Queries {
140+ out = append (out , newAnalyzedQuery (q , includeAST ))
94141 }
95- if err := tmp .Close (); err != nil {
96- return fmt .Errorf ("failed to close temp file: %w" , err )
142+
143+ stdout := cmd .OutOrStdout ()
144+ encoder := json .NewEncoder (stdout )
145+ encoder .SetIndent ("" , " " )
146+ if err := encoder .Encode (out ); err != nil {
147+ return fmt .Errorf ("failed to encode analysis: %w" , err )
97148 }
98- queryPath = tmp .Name ()
99- }
100-
101- var engine config.Engine
102- switch dialect {
103- case "postgresql" , "postgres" , "pg" :
104- engine = config .EnginePostgreSQL
105- case "mysql" :
106- engine = config .EngineMySQL
107- case "sqlite" :
108- engine = config .EngineSQLite
109- default :
110- return fmt .Errorf ("unsupported dialect: %s (use postgresql, mysql, or sqlite)" , dialect )
111- }
112-
113- sql := config.SQL {
114- Engine : engine ,
115- Schema : config.Paths {schemaPath },
116- Queries : config.Paths {queryPath },
117- }
118- combo := config .Combine (config.Config {}, sql )
119- parserOpts := opts.Parser {}
120-
121- ctx := cmd .Context ()
122- c , err := compiler .NewCompiler (sql , combo , parserOpts )
123- if err != nil {
124- return fmt .Errorf ("error creating compiler: %w" , err )
125- }
126- defer c .Close (ctx )
127-
128- if err := c .ParseCatalog (sql .Schema ); err != nil {
129- return fmt .Errorf ("error parsing schema: %w" , formatParseError (err ))
130- }
131- if err := c .ParseQueries (sql .Queries , parserOpts ); err != nil {
132- return fmt .Errorf ("error parsing queries: %w" , formatParseError (err ))
133- }
134-
135- result := c .Result ()
136-
137- out := make ([]analyzedQuery , 0 , len (result .Queries ))
138- for _ , q := range result .Queries {
139- out = append (out , newAnalyzedQuery (q , includeAST ))
140- }
141-
142- stdout := cmd .OutOrStdout ()
143- encoder := json .NewEncoder (stdout )
144- encoder .SetIndent ("" , " " )
145- if err := encoder .Encode (out ); err != nil {
146- return fmt .Errorf ("failed to encode analysis: %w" , err )
147- }
148-
149- return nil
150- },
149+
150+ return nil
151+ },
152+ }
153+ cmd .Flags ().StringP ("dialect" , "d" , "" , "SQL dialect to use (postgresql, mysql, or sqlite)" )
154+ cmd .Flags ().StringP ("schema" , "s" , "" , "path to the schema file" )
155+ cmd .Flags ().BoolP ("ast" , "" , false , "include the statement AST in the output" )
156+ return cmd
151157}
152158
153159// formatParseError unwraps a multierr.Error into a single error containing all
0 commit comments