snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
This commit is contained in:
166
internal/cmd/parse.go
Normal file
166
internal/cmd/parse.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/clickhouse"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/dolphin"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/duckdb"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/googlesql"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/mssql"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/postgresql"
|
||||
"github.com/sqlc-dev/sqlc/internal/engine/sqlite"
|
||||
"github.com/sqlc-dev/sqlc/internal/metadata"
|
||||
"github.com/sqlc-dev/sqlc/internal/source"
|
||||
"github.com/sqlc-dev/sqlc/internal/sql/ast"
|
||||
)
|
||||
|
||||
// dialectParser is the subset of the engine parsers that the parse command
|
||||
// needs: parsing SQL into statements and reporting the dialect's comment syntax
|
||||
// (used to extract the sqlc query name and command).
|
||||
type dialectParser interface {
|
||||
Parse(io.Reader) ([]ast.Statement, error)
|
||||
CommentSyntax() source.CommentSyntax
|
||||
}
|
||||
|
||||
// parsedStatement is the JSON representation of a single parsed statement. The
|
||||
// name and cmd are extracted from the sqlc query annotation (e.g.
|
||||
// "-- name: GetAuthor :one") and are omitted when the statement has none.
|
||||
type parsedStatement struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Cmd string `json:"cmd,omitempty"`
|
||||
AST *ast.RawStmt `json:"ast"`
|
||||
}
|
||||
|
||||
func newParseCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "parse [file]",
|
||||
Short: "Parse SQL and output the AST as JSON",
|
||||
Long: `Parse SQL from a file or stdin and output the abstract syntax tree as JSON.
|
||||
|
||||
Each statement is reported with its sqlc query name and command (when the
|
||||
statement carries a "-- name:" annotation) alongside the AST.
|
||||
|
||||
Examples:
|
||||
# Parse a SQL file with PostgreSQL dialect
|
||||
sqlc parse --dialect postgresql schema.sql
|
||||
|
||||
# Parse from stdin with MySQL dialect
|
||||
echo "SELECT * FROM users" | sqlc parse --dialect mysql
|
||||
|
||||
# Parse SQLite SQL
|
||||
sqlc parse --dialect sqlite queries.sql
|
||||
|
||||
# Parse ClickHouse SQL
|
||||
sqlc parse --dialect clickhouse queries.sql
|
||||
|
||||
# Parse GoogleSQL (BigQuery, Spanner)
|
||||
sqlc parse --dialect googlesql queries.sql
|
||||
|
||||
# Parse SQL Server (T-SQL) SQL
|
||||
sqlc parse --dialect mssql queries.sql
|
||||
|
||||
# Parse DuckDB SQL
|
||||
sqlc parse --dialect duckdb queries.sql`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
dialect, err := cmd.Flags().GetString("dialect")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if dialect == "" {
|
||||
return fmt.Errorf("--dialect flag is required (postgresql, mysql, sqlite, clickhouse, googlesql, mssql, or duckdb)")
|
||||
}
|
||||
|
||||
// Determine input source
|
||||
var input io.Reader
|
||||
if len(args) == 1 {
|
||||
file, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
input = file
|
||||
} else {
|
||||
// Check if stdin has data
|
||||
stat, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat stdin: %w", err)
|
||||
}
|
||||
if (stat.Mode() & os.ModeCharDevice) != 0 {
|
||||
return fmt.Errorf("no input provided. Specify a file path or pipe SQL via stdin")
|
||||
}
|
||||
input = cmd.InOrStdin()
|
||||
}
|
||||
|
||||
// Select the parser for the requested dialect
|
||||
var parser dialectParser
|
||||
switch dialect {
|
||||
case "postgresql", "postgres", "pg":
|
||||
parser = postgresql.NewParser()
|
||||
case "mysql":
|
||||
parser = dolphin.NewParser()
|
||||
case "sqlite":
|
||||
parser = sqlite.NewParser()
|
||||
case "clickhouse":
|
||||
parser = clickhouse.NewParser()
|
||||
case "googlesql":
|
||||
parser = googlesql.NewParser()
|
||||
case "mssql", "sqlserver":
|
||||
parser = mssql.NewParser()
|
||||
case "duckdb":
|
||||
parser = duckdb.NewParser()
|
||||
default:
|
||||
return fmt.Errorf("unsupported dialect: %s (use postgresql, mysql, sqlite, clickhouse, googlesql, mssql, or duckdb)", dialect)
|
||||
}
|
||||
|
||||
// Read the full source so each statement's name and command can be
|
||||
// extracted from its annotation comment.
|
||||
src, err := io.ReadAll(input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read input: %w", err)
|
||||
}
|
||||
|
||||
stmts, err := parser.Parse(strings.NewReader(string(src)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse error: %w", err)
|
||||
}
|
||||
|
||||
commentSyntax := metadata.CommentSyntax(parser.CommentSyntax())
|
||||
|
||||
// Output the AST as a single JSON document
|
||||
out := make([]parsedStatement, 0, len(stmts))
|
||||
for _, stmt := range stmts {
|
||||
ps := parsedStatement{AST: stmt.Raw}
|
||||
rawSQL, err := source.Pluck(string(src), stmt.Raw.StmtLocation, stmt.Raw.StmtLen)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read statement source: %w", err)
|
||||
}
|
||||
name, cmd, err := metadata.ParseQueryNameAndType(rawSQL, commentSyntax)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse query annotation: %w", err)
|
||||
}
|
||||
ps.Name = name
|
||||
ps.Cmd = cmd
|
||||
out = append(out, ps)
|
||||
}
|
||||
|
||||
stdout := cmd.OutOrStdout()
|
||||
encoder := json.NewEncoder(stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(out); err != nil {
|
||||
return fmt.Errorf("failed to encode AST: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringP("dialect", "d", "", "SQL dialect to use (postgresql, mysql, sqlite, clickhouse, googlesql, mssql, or duckdb)")
|
||||
return cmd
|
||||
}
|
||||
Reference in New Issue
Block a user