Files
upstream-mirror 0f4df23290
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
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

278 lines
6.8 KiB
Go

package cmd
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"runtime/trace"
"github.com/cubicdaiya/gonp"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"gopkg.in/yaml.v3"
"github.com/sqlc-dev/sqlc/internal/config"
"github.com/sqlc-dev/sqlc/internal/info"
"github.com/sqlc-dev/sqlc/internal/opts"
"github.com/sqlc-dev/sqlc/internal/sqlcdebug"
"github.com/sqlc-dev/sqlc/internal/tracer"
)
var debugProcessPlugins = sqlcdebug.New("processplugins")
func init() {
createDBCmd.Flags().StringP("queryset", "", "", "name of the queryset to use")
pushCmd.Flags().BoolP("dry-run", "", false, "dump push request (default: false)")
initCmd.Flags().BoolP("v1", "", false, "generate v1 config yaml file")
initCmd.Flags().BoolP("v2", "", true, "generate v2 config yaml file")
initCmd.MarkFlagsMutuallyExclusive("v1", "v2")
}
// Do runs the command logic.
func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
rootCmd := &cobra.Command{Use: "sqlc", SilenceUsage: true}
rootCmd.PersistentFlags().StringP("file", "f", "", "specify an alternate config file (default: sqlc.yaml)")
rootCmd.AddCommand(checkCmd)
rootCmd.AddCommand(createDBCmd)
rootCmd.AddCommand(diffCmd)
rootCmd.AddCommand(genCmd)
rootCmd.AddCommand(newFmtCmd())
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(newParseCmd())
rootCmd.AddCommand(newAnalyzeCmd())
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(verifyCmd)
rootCmd.AddCommand(pushCmd)
rootCmd.AddCommand(NewCmdVet())
rootCmd.SetArgs(args)
rootCmd.SetIn(stdin)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)
ctx := context.Background()
if tracer.Path() != "" {
tracectx, cleanup, err := tracer.Start(ctx)
if err != nil {
fmt.Printf("failed to start trace: %v\n", err)
return 1
}
ctx = tracectx
defer cleanup()
}
if err := rootCmd.ExecuteContext(ctx); err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
return exitError.ExitCode()
} else {
return 1
}
}
return 0
}
var version string
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the sqlc version number",
RunE: func(cmd *cobra.Command, args []string) error {
defer trace.StartRegion(cmd.Context(), "version").End()
if version == "" {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", info.Version)
} else {
fmt.Fprintf(cmd.OutOrStdout(), "%s\n", version)
}
return nil
},
}
var initCmd = &cobra.Command{
Use: "init",
Short: "Create an empty sqlc.yaml settings file",
RunE: func(cmd *cobra.Command, args []string) error {
useV1, err := cmd.Flags().GetBool("v1")
if err != nil {
return err
}
var yamlConfig any
if useV1 {
yamlConfig = config.V1GenerateSettings{Version: "1"}
} else {
yamlConfig = config.Config{Version: "2"}
}
defer trace.StartRegion(cmd.Context(), "init").End()
file := "sqlc.yaml"
if f := cmd.Flag("file"); f != nil && f.Changed {
file = f.Value.String()
if file == "" {
return fmt.Errorf("file argument is empty")
}
}
if _, err := os.Stat(file); !os.IsNotExist(err) {
fmt.Printf("%s is already created\n", file)
return nil
}
blob, err := yaml.Marshal(yamlConfig)
if err != nil {
return err
}
err = os.WriteFile(file, blob, 0644)
if err != nil {
return err
}
configDoc := "https://docs.sqlc.dev/en/stable/reference/config.html"
fmt.Printf(
"%s is added. Please visit %s to learn more about configuration\n",
file,
configDoc,
)
return nil
},
}
type Env struct {
DryRun bool
Experiment opts.Experiment
}
func ParseEnv(c *cobra.Command) Env {
dr := c.Flag("dry-run")
return Env{
DryRun: dr != nil && dr.Changed,
Experiment: opts.ExperimentFromEnv(),
}
}
var ErrPluginProcessDisabled = errors.New("plugin: process-based plugins disabled via SQLCDEBUG=processplugins=0")
func (e *Env) Validate(cfg *config.Config) error {
for _, plugin := range cfg.Plugins {
if plugin.Process != nil && debugProcessPlugins.Value() == "0" {
return ErrPluginProcessDisabled
}
}
return nil
}
func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) {
if f != nil && f.Changed {
file := f.Value.String()
if file == "" {
fmt.Fprintln(stderr, "error parsing config: file argument is empty")
os.Exit(1)
}
abspath, err := filepath.Abs(file)
if err != nil {
fmt.Fprintf(stderr, "error parsing config: absolute file path lookup failed: %s\n", err)
os.Exit(1)
}
return filepath.Dir(abspath), filepath.Base(abspath)
} else {
wd, err := os.Getwd()
if err != nil {
fmt.Fprintln(stderr, "error parsing sqlc.json: file does not exist")
os.Exit(1)
}
return wd, ""
}
}
var genCmd = &cobra.Command{
Use: "generate",
Short: "Generate source code from SQL",
RunE: func(cmd *cobra.Command, args []string) error {
defer trace.StartRegion(cmd.Context(), "generate").End()
stderr := cmd.ErrOrStderr()
dir, name := getConfigPath(stderr, cmd.Flag("file"))
output, err := Generate(cmd.Context(), dir, name, &Options{
Env: ParseEnv(cmd),
Stderr: stderr,
})
if err != nil {
os.Exit(1)
}
defer trace.StartRegion(cmd.Context(), "writefiles").End()
for filename, source := range output {
os.MkdirAll(filepath.Dir(filename), 0755)
if err := os.WriteFile(filename, []byte(source), 0644); err != nil {
fmt.Fprintf(stderr, "%s: %s\n", filename, err)
return err
}
}
return nil
},
}
var checkCmd = &cobra.Command{
Use: "compile",
Short: "Statically check SQL for syntax and type errors",
RunE: func(cmd *cobra.Command, args []string) error {
defer trace.StartRegion(cmd.Context(), "compile").End()
stderr := cmd.ErrOrStderr()
dir, name := getConfigPath(stderr, cmd.Flag("file"))
_, err := Generate(cmd.Context(), dir, name, &Options{
Env: ParseEnv(cmd),
Stderr: stderr,
})
if err != nil {
os.Exit(1)
}
return nil
},
}
func getLines(f []byte) []string {
fp := bytes.NewReader(f)
scanner := bufio.NewScanner(fp)
lines := make([]string, 0)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
return lines
}
func filterHunks[T gonp.Elem](uniHunks []gonp.UniHunk[T]) []gonp.UniHunk[T] {
var out []gonp.UniHunk[T]
for i, uniHunk := range uniHunks {
var changed bool
for _, e := range uniHunk.GetChanges() {
switch e.GetType() {
case gonp.SesDelete:
changed = true
case gonp.SesAdd:
changed = true
}
}
if changed {
out = append(out, uniHunks[i])
}
}
return out
}
var diffCmd = &cobra.Command{
Use: "diff",
Short: "Compare the generated files to the existing files",
RunE: func(cmd *cobra.Command, args []string) error {
defer trace.StartRegion(cmd.Context(), "diff").End()
stderr := cmd.ErrOrStderr()
dir, name := getConfigPath(stderr, cmd.Flag("file"))
opts := &Options{
Env: ParseEnv(cmd),
Stderr: stderr,
}
if err := Diff(cmd.Context(), dir, name, opts); err != nil {
os.Exit(1)
}
return nil
},
}