Skip to content

Commit 92ad2e3

Browse files
kyleconroycmoog
andauthored
Add MySQL support (#230)
With this commit, sqlc now supports two database engines: PostgreSQL and MySQL. The MySQL support contained in this change is experimental. It still fails to parse many valid queries, and the code generated for some queries is incorrect. Also, the interface between parsing, codegen, and database catalog is a bit of a mess. The goal of merging this support in now is to make progress towards 1.0 Co-authored-by: Charles Moog <[email protected]>
1 parent 11a21ad commit 92ad2e3

23 files changed

+2264
-166
lines changed

README.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ Each package document has the following keys:
307307
- Directory of SQL queries or path to single SQL file
308308
- `schema`:
309309
- Directory of SQL migrations or path to single SQL file
310+
- `engine`:
311+
- Either `postgresql` or `mysql`. Defaults to `postgresql`. MySQL support is experimental
310312

311313
### Type Overrides
312314

@@ -411,13 +413,17 @@ Each commit is deployed to the [`devel` channel on Equinox](https://dl.equinox.i
411413
- [Linux](https://bin.equinox.io/c/gvM95th6ps1/sqlc-devel-linux-amd64.tgz)
412414
- [macOS](https://bin.equinox.io/c/gvM95th6ps1/sqlc-devel-darwin-amd64.zip)
413415

414-
## Other Database Engines
416+
## Other Databases and Languages
415417

416-
sqlc currently only supports PostgreSQL. If you'd like to support another database, we'd welcome a contribution.
418+
sqlc currently only supports PostgreSQL / Go. MySQL support has been merged,
419+
but it's marked as experimental. SQLite and TypeScript support are planned.
417420

418-
## Other Language Backends
421+
| Language | PostgreSQL | MySQL | SQLite |
422+
| ------------ |:----------------:|:----------------:|:----------------:|
423+
| Go |:white_check_mark:|:warning: |:timer_clock: |
424+
| TypeScript |:timer_clock: |:timer_clock: |:timer_clock: |
419425

420-
sqlc currently only generates Go code, but if you'd like to build another language backend, we'd welcome a contribution.
426+
If you'd like to add another database or language, we'd welcome a contribution.
421427

422428
## Acknowledgements
423429

go.mod

+6
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,10 @@ require (
88
github.com/jinzhu/inflection v1.0.0
99
github.com/lfittl/pg_query_go v1.0.0
1010
github.com/spf13/cobra v0.0.5
11+
github.com/spf13/pflag v1.0.5 // indirect
12+
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
13+
golang.org/x/sys v0.0.0-20191220220014-0732a990476f // indirect
14+
google.golang.org/genproto v0.0.0-20191223191004-3caeed10a8bf // indirect
15+
google.golang.org/grpc v1.26.0 // indirect
16+
vitess.io/vitess v0.0.0-20191113025808-0629f0da20ab
1117
)

go.sum

+289
Large diffs are not rendered by default.

internal/cmd/cmd.go

+45-35
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"path/filepath"
1212

1313
"github.com/kyleconroy/sqlc/internal/dinosql"
14+
"github.com/kyleconroy/sqlc/internal/mysql"
1415

1516
"github.com/davecgh/go-spew/spew"
1617
pg "github.com/lfittl/pg_query_go"
@@ -126,48 +127,57 @@ var genCmd = &cobra.Command{
126127

127128
output := map[string]string{}
128129

129-
for i, pkg := range settings.Packages {
130+
for _, pkg := range settings.Packages {
130131
name := pkg.Name
131132

132-
if pkg.Path == "" {
133-
fmt.Fprintf(os.Stderr, "package[%d]: path must be set\n", i)
134-
errored = true
135-
continue
136-
}
133+
var result dinosql.Generateable
137134

138-
if name == "" {
139-
name = filepath.Base(pkg.Path)
140-
}
135+
switch pkg.Engine {
141136

142-
c, err := dinosql.ParseCatalog(pkg.Schema)
143-
if err != nil {
144-
fmt.Fprintf(os.Stderr, "# package %s\n", name)
145-
if parserErr, ok := err.(*dinosql.ParserErr); ok {
146-
for _, fileErr := range parserErr.Errs {
147-
fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n", fileErr.Filename, fileErr.Line, fileErr.Column, fileErr.Err)
137+
case dinosql.EngineMySQL:
138+
// Experimental MySQL support
139+
q, err := mysql.GeneratePkg(name, pkg.Schema, pkg.Queries, settings)
140+
if err != nil {
141+
fmt.Fprintf(os.Stderr, "# package %s\n", name)
142+
fmt.Fprintf(os.Stderr, "error parsing file: %s\n", err)
143+
errored = true
144+
continue
145+
}
146+
result = q
147+
148+
case dinosql.EnginePostgreSQL:
149+
c, err := dinosql.ParseCatalog(pkg.Schema)
150+
if err != nil {
151+
fmt.Fprintf(os.Stderr, "# package %s\n", name)
152+
if parserErr, ok := err.(*dinosql.ParserErr); ok {
153+
for _, fileErr := range parserErr.Errs {
154+
fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n", fileErr.Filename, fileErr.Line, fileErr.Column, fileErr.Err)
155+
}
156+
} else {
157+
fmt.Fprintf(os.Stderr, "error parsing schema: %s\n", err)
148158
}
149-
} else {
150-
fmt.Fprintf(os.Stderr, "error parsing schema: %s\n", err)
159+
errored = true
160+
continue
151161
}
152-
errored = true
153-
continue
154-
}
155162

156-
q, err := dinosql.ParseQueries(c, settings, pkg)
157-
if err != nil {
158-
fmt.Fprintf(os.Stderr, "# package %s\n", name)
159-
if parserErr, ok := err.(*dinosql.ParserErr); ok {
160-
for _, fileErr := range parserErr.Errs {
161-
fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n", fileErr.Filename, fileErr.Line, fileErr.Column, fileErr.Err)
163+
q, err := dinosql.ParseQueries(c, pkg)
164+
if err != nil {
165+
fmt.Fprintf(os.Stderr, "# package %s\n", name)
166+
if parserErr, ok := err.(*dinosql.ParserErr); ok {
167+
for _, fileErr := range parserErr.Errs {
168+
fmt.Fprintf(os.Stderr, "%s:%d:%d: %s\n", fileErr.Filename, fileErr.Line, fileErr.Column, fileErr.Err)
169+
}
170+
} else {
171+
fmt.Fprintf(os.Stderr, "error parsing queries: %s\n", err)
162172
}
163-
} else {
164-
fmt.Fprintf(os.Stderr, "error parsing queries: %s\n", err)
173+
errored = true
174+
continue
165175
}
166-
errored = true
167-
continue
176+
result = q
177+
168178
}
169179

170-
files, err := dinosql.Generate(q, settings, pkg)
180+
files, err := dinosql.Generate(result, settings)
171181
if err != nil {
172182
fmt.Fprintf(os.Stderr, "# package %s\n", name)
173183
fmt.Fprintf(os.Stderr, "error generating code: %s\n", err)
@@ -199,13 +209,13 @@ var checkCmd = &cobra.Command{
199209
Use: "compile",
200210
Short: "Statically check SQL for syntax and type errors",
201211
RunE: func(cmd *cobra.Command, args []string) error {
202-
blob, err := ioutil.ReadFile("sqlc.json")
212+
file, err := os.Open("sqlc.json")
203213
if err != nil {
204214
return err
205215
}
206216

207-
var settings dinosql.GenerateSettings
208-
if err := json.Unmarshal(blob, &settings); err != nil {
217+
settings, err := dinosql.ParseConfig(file)
218+
if err != nil {
209219
return err
210220
}
211221

@@ -214,7 +224,7 @@ var checkCmd = &cobra.Command{
214224
if err != nil {
215225
return err
216226
}
217-
if _, err := dinosql.ParseQueries(c, settings, pkg); err != nil {
227+
if _, err := dinosql.ParseQueries(c, pkg); err != nil {
218228
return err
219229
}
220230
}

internal/dinosql/checks_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
func TestFuncs(t *testing.T) {
1313
_, err := ParseQueries(
1414
pg.NewCatalog(),
15-
GenerateSettings{},
1615
PackageSettings{
1716
Queries: filepath.Join("testdata", "funcs"),
1817
},

internal/dinosql/config.go

+57-5
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,45 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"path/filepath"
89
"strings"
910

1011
"github.com/kyleconroy/sqlc/internal/pg"
1112
)
1213

14+
const errMessageNoVersion = `The configuration file must have a version number.
15+
Set the version to 1 at the top of sqlc.json:
16+
17+
{
18+
"version": "1"
19+
...
20+
}
21+
`
22+
23+
const errMessageUnknownVersion = `The configuration file has an invalid version number.
24+
The only supported version is "1".
25+
`
26+
27+
const errMessageNoPackages = `No packages are configured`
28+
1329
type GenerateSettings struct {
14-
Version string `json:"version"`
15-
Packages []PackageSettings `json:"packages"`
16-
Overrides []Override `json:"overrides,omitempty"`
17-
Rename map[string]string `json:"rename,omitempty"`
30+
Version string `json:"version"`
31+
Packages []PackageSettings `json:"packages"`
32+
Overrides []Override `json:"overrides,omitempty"`
33+
Rename map[string]string `json:"rename,omitempty"`
34+
PackageMap map[string]PackageSettings
1835
}
1936

37+
type Engine string
38+
39+
const (
40+
EngineMySQL Engine = "mysql"
41+
EnginePostgreSQL Engine = "postgresql"
42+
)
43+
2044
type PackageSettings struct {
2145
Name string `json:"name"`
46+
Engine Engine `json:"engine,omitempty"`
2247
Path string `json:"path"`
2348
Schema string `json:"schema"`
2449
Queries string `json:"queries"`
@@ -96,6 +121,8 @@ func (o *Override) Parse() error {
96121
var ErrMissingVersion = errors.New("no version number")
97122
var ErrUnknownVersion = errors.New("invalid version number")
98123
var ErrNoPackages = errors.New("no packages")
124+
var ErrNoPackageName = errors.New("missing package name")
125+
var ErrNoPackagePath = errors.New("missing package path")
99126

100127
func ParseConfig(rd io.Reader) (GenerateSettings, error) {
101128
dec := json.NewDecoder(rd)
@@ -119,11 +146,36 @@ func ParseConfig(rd io.Reader) (GenerateSettings, error) {
119146
}
120147
}
121148
for j := range config.Packages {
149+
if config.Packages[j].Path == "" {
150+
return config, ErrNoPackagePath
151+
}
122152
for i := range config.Packages[j].Overrides {
123153
if err := config.Packages[j].Overrides[i].Parse(); err != nil {
124154
return config, err
125155
}
126156
}
157+
if config.Packages[j].Name == "" {
158+
config.Packages[j].Name = filepath.Base(config.Packages[j].Path)
159+
}
160+
if config.Packages[j].Engine == "" {
161+
config.Packages[j].Engine = EnginePostgreSQL
162+
}
127163
}
128-
return config, nil
164+
err := config.PopulatePkgMap()
165+
166+
return config, err
167+
}
168+
169+
func (s *GenerateSettings) PopulatePkgMap() error {
170+
packageMap := make(map[string]PackageSettings)
171+
172+
for _, c := range s.Packages {
173+
if c.Name == "" {
174+
return ErrNoPackageName
175+
}
176+
packageMap[c.Name] = c
177+
}
178+
s.PackageMap = packageMap
179+
180+
return nil
129181
}

0 commit comments

Comments
 (0)