123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- package cmd
- import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "github.com/kyleconroy/sqlc/internal/codegen/golang"
- "github.com/kyleconroy/sqlc/internal/codegen/kotlin"
- "github.com/kyleconroy/sqlc/internal/compiler"
- "github.com/kyleconroy/sqlc/internal/config"
- "github.com/kyleconroy/sqlc/internal/multierr"
- "github.com/kyleconroy/sqlc/internal/mysql"
- "github.com/kyleconroy/sqlc/internal/opts"
- )
- const errMessageNoVersion = `The configuration file must have a version number.
- Set the version to 1 at the top of sqlc.json:
- {
- "version": "1"
- ...
- }
- `
- const errMessageUnknownVersion = `The configuration file has an invalid version number.
- The only supported version is "1".
- `
- const errMessageNoPackages = `No packages are configured`
- func printFileErr(stderr io.Writer, dir string, fileErr *multierr.FileError) {
- filename := strings.TrimPrefix(fileErr.Filename, dir+"/")
- fmt.Fprintf(stderr, "%s:%d:%d: %s\n", filename, fileErr.Line, fileErr.Column, fileErr.Err)
- }
- type outPair struct {
- Gen config.SQLGen
- config.SQL
- }
- func Generate(e Env, dir string, stderr io.Writer) (map[string]string, error) {
- var yamlMissing, jsonMissing bool
- yamlPath := filepath.Join(dir, "sqlc.yaml")
- jsonPath := filepath.Join(dir, "sqlc.json")
- if _, err := os.Stat(yamlPath); os.IsNotExist(err) {
- yamlMissing = true
- }
- if _, err := os.Stat(jsonPath); os.IsNotExist(err) {
- jsonMissing = true
- }
- if yamlMissing && jsonMissing {
- fmt.Fprintln(stderr, "error parsing sqlc.json: file does not exist")
- return nil, errors.New("config file missing")
- }
- if !yamlMissing && !jsonMissing {
- fmt.Fprintln(stderr, "error parsing sqlc.json: both files present")
- return nil, errors.New("sqlc.json and sqlc.yaml present")
- }
- configPath := yamlPath
- if yamlMissing {
- configPath = jsonPath
- }
- blob, err := ioutil.ReadFile(configPath)
- if err != nil {
- fmt.Fprintln(stderr, "error parsing sqlc.json: file does not exist")
- return nil, err
- }
- conf, err := config.ParseConfig(bytes.NewReader(blob))
- if err != nil {
- switch err {
- case config.ErrMissingVersion:
- fmt.Fprintf(stderr, errMessageNoVersion)
- case config.ErrUnknownVersion:
- fmt.Fprintf(stderr, errMessageUnknownVersion)
- case config.ErrNoPackages:
- fmt.Fprintf(stderr, errMessageNoPackages)
- }
- fmt.Fprintf(stderr, "error parsing sqlc.json: %s\n", err)
- return nil, err
- }
- output := map[string]string{}
- errored := false
- var pairs []outPair
- for _, sql := range conf.SQL {
- if sql.Gen.Go != nil {
- pairs = append(pairs, outPair{
- SQL: sql,
- Gen: config.SQLGen{Go: sql.Gen.Go},
- })
- }
- if sql.Gen.Kotlin != nil {
- pairs = append(pairs, outPair{
- SQL: sql,
- Gen: config.SQLGen{Kotlin: sql.Gen.Kotlin},
- })
- }
- }
- for _, sql := range pairs {
- combo := config.Combine(conf, sql.SQL)
- // TODO: This feels like a hack that will bite us later
- joined := make([]string, 0, len(sql.Schema))
- for _, s := range sql.Schema {
- joined = append(joined, filepath.Join(dir, s))
- }
- sql.Schema = joined
- joined = make([]string, 0, len(sql.Queries))
- for _, q := range sql.Queries {
- joined = append(joined, filepath.Join(dir, q))
- }
- sql.Queries = joined
- var name string
- parseOpts := opts.Parser{}
- if sql.Gen.Go != nil {
- name = combo.Go.Package
- } else if sql.Gen.Kotlin != nil {
- parseOpts.UsePositionalParameters = true
- name = combo.Kotlin.Package
- }
- var files map[string]string
- var out string
- // TODO: Note about how this will be going away
- if sql.Engine == config.EngineMySQL {
- result, errored := parseMySQL(e, name, dir, sql.SQL, combo, parseOpts, stderr)
- if errored {
- break
- }
- out = combo.Go.Out
- files, err = golang.DeprecatedGenerate(result, combo)
- } else {
- result, errored := parse(e, name, dir, sql.SQL, combo, parseOpts, stderr)
- if errored {
- break
- }
- switch {
- case sql.Gen.Go != nil:
- out = combo.Go.Out
- files, err = golang.Generate(result, combo)
- case sql.Gen.Kotlin != nil:
- out = combo.Kotlin.Out
- files, err = kotlin.Generate(result, combo)
- default:
- panic("missing language backend")
- }
- }
- if err != nil {
- fmt.Fprintf(stderr, "# package %s\n", name)
- fmt.Fprintf(stderr, "error generating code: %s\n", err)
- errored = true
- continue
- }
- for n, source := range files {
- filename := filepath.Join(dir, out, n)
- output[filename] = source
- }
- }
- if errored {
- return nil, fmt.Errorf("errored")
- }
- return output, nil
- }
- // Experimental MySQL support
- func parseMySQL(e Env, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (golang.Generateable, bool) {
- q, err := mysql.GeneratePkg(name, sql.Schema, sql.Queries, combo)
- if err != nil {
- fmt.Fprintf(stderr, "# package %s\n", name)
- if parserErr, ok := err.(*multierr.Error); ok {
- for _, fileErr := range parserErr.Errs() {
- printFileErr(stderr, dir, fileErr)
- }
- } else {
- fmt.Fprintf(stderr, "error parsing schema: %s\n", err)
- }
- return nil, true
- }
- return q, false
- }
- func parse(e Env, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) {
- eng := compiler.NewEngine(sql, combo)
- if err := eng.ParseCatalog(sql.Schema); err != nil {
- fmt.Fprintf(stderr, "# package %s\n", name)
- if parserErr, ok := err.(*multierr.Error); ok {
- for _, fileErr := range parserErr.Errs() {
- printFileErr(stderr, dir, fileErr)
- }
- } else {
- fmt.Fprintf(stderr, "error parsing schema: %s\n", err)
- }
- return nil, true
- }
- if err := eng.ParseQueries(sql.Queries, parserOpts); err != nil {
- fmt.Fprintf(stderr, "# package %s\n", name)
- if parserErr, ok := err.(*multierr.Error); ok {
- for _, fileErr := range parserErr.Errs() {
- printFileErr(stderr, dir, fileErr)
- }
- } else {
- fmt.Fprintf(stderr, "error parsing queries: %s\n", err)
- }
- return nil, true
- }
- return eng.Result(), false
- }
|