Skip to content

Commit eecb559

Browse files
authored
MySQL experimental parser (#525)
* update go.mod * go mod tidy * Let's crush some bugs * rm build result * A bunch of broken tests * Add comment style to parser * Add syntax error tests
1 parent fd53ba1 commit eecb559

File tree

33 files changed

+573
-559
lines changed

33 files changed

+573
-559
lines changed

go.mod

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@ require (
77
github.com/davecgh/go-spew v1.1.1
88
github.com/google/go-cmp v0.3.0
99
github.com/jinzhu/inflection v1.0.0
10-
github.com/kyleconroy/sqlc-testdata v0.0.0-20200512000015-15313bc43553
1110
github.com/lfittl/pg_query_go v1.0.0
1211
github.com/lib/pq v1.4.0
13-
github.com/pingcap/parser v0.0.0-20200218113622-517beb2e39c2
14-
github.com/pingcap/tidb v1.1.0-beta.0.20200219045929-1344d6ddd9e7
12+
github.com/pingcap/parser v3.1.1+incompatible
13+
github.com/pingcap/tidb v1.1.0-beta.0.20200426094148-df4c9f05c49c
1514
github.com/spf13/cobra v0.0.5
16-
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
17-
golang.org/x/sys v0.0.0-20191220220014-0732a990476f // indirect
1815
google.golang.org/genproto v0.0.0-20191223191004-3caeed10a8bf // indirect
1916
google.golang.org/grpc v1.26.0 // indirect
2017
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71

go.sum

Lines changed: 178 additions & 79 deletions
Large diffs are not rendered by default.

internal/cmd/generate.go

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,7 @@ func (d *dinosqlEngine) Result() golang.Generateable {
209209
}
210210

211211
func parse(e Env, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts dinosql.ParserOpts, stderr io.Writer) (golang.Generateable, bool) {
212-
switch sql.Engine {
213-
case config.EngineMySQL:
212+
if sql.Engine == config.EngineMySQL {
214213
// Experimental MySQL support
215214
q, err := mysql.GeneratePkg(name, sql.Schema, sql.Queries, combo)
216215
if err != nil {
@@ -225,48 +224,36 @@ func parse(e Env, name, dir string, sql config.SQL, combo config.CombinedSetting
225224
return nil, true
226225
}
227226
return q, false
227+
}
228228

229-
case config.EnginePostgreSQL, config.EngineXLemon:
230-
var eng postgreEngine
231-
if e.ExperimentalParser || sql.Engine == config.EngineXLemon {
232-
eng = compiler.NewEngine(sql, combo)
233-
} else {
234-
eng = &dinosqlEngine{}
235-
}
236-
if err := eng.ParseCatalog(sql.Schema); err != nil {
237-
fmt.Fprintf(stderr, "# package %s\n", name)
238-
if parserErr, ok := err.(*multierr.Error); ok {
239-
for _, fileErr := range parserErr.Errs() {
240-
printFileErr(stderr, dir, fileErr)
241-
}
242-
} else {
243-
fmt.Fprintf(stderr, "error parsing schema: %s\n", err)
229+
var eng postgreEngine
230+
if sql.Engine == config.EnginePostgreSQL && !e.ExperimentalParser {
231+
eng = &dinosqlEngine{}
232+
} else {
233+
eng = compiler.NewEngine(sql, combo)
234+
}
235+
236+
if err := eng.ParseCatalog(sql.Schema); err != nil {
237+
fmt.Fprintf(stderr, "# package %s\n", name)
238+
if parserErr, ok := err.(*multierr.Error); ok {
239+
for _, fileErr := range parserErr.Errs() {
240+
printFileErr(stderr, dir, fileErr)
244241
}
245-
return nil, true
242+
} else {
243+
fmt.Fprintf(stderr, "error parsing schema: %s\n", err)
246244
}
247-
if err := eng.ParseQueries(sql.Queries, parserOpts); err != nil {
248-
fmt.Fprintf(stderr, "# package %s\n", name)
249-
if parserErr, ok := err.(*multierr.Error); ok {
250-
for _, fileErr := range parserErr.Errs() {
251-
printFileErr(stderr, dir, fileErr)
252-
}
253-
} else {
254-
fmt.Fprintf(stderr, "error parsing queries: %s\n", err)
245+
return nil, true
246+
}
247+
if err := eng.ParseQueries(sql.Queries, parserOpts); err != nil {
248+
fmt.Fprintf(stderr, "# package %s\n", name)
249+
if parserErr, ok := err.(*multierr.Error); ok {
250+
for _, fileErr := range parserErr.Errs() {
251+
printFileErr(stderr, dir, fileErr)
255252
}
256-
return nil, true
257-
}
258-
return eng.Result(), false
259-
260-
case config.EngineXDolphin, config.EngineXElephant:
261-
r, err := compiler.Run(sql, combo)
262-
if err != nil {
263-
fmt.Fprintf(stderr, "# package %s\n", name)
264-
fmt.Fprintf(stderr, "error: %s\n", err)
265-
return nil, true
253+
} else {
254+
fmt.Fprintf(stderr, "error parsing queries: %s\n", err)
266255
}
267-
return r, false
268-
269-
default:
270-
panic("invalid engine")
256+
return nil, true
271257
}
258+
return eng.Result(), false
272259
}

internal/compiler/compile.go

Lines changed: 2 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,20 @@ import (
77
"io/ioutil"
88
"path/filepath"
99
"regexp"
10-
"sort"
1110
"strings"
1211

13-
"github.com/kyleconroy/sqlc/internal/codegen/golang"
14-
"github.com/kyleconroy/sqlc/internal/config"
15-
"github.com/kyleconroy/sqlc/internal/dolphin"
12+
"github.com/kyleconroy/sqlc/internal/metadata"
1613
"github.com/kyleconroy/sqlc/internal/migrations"
1714
"github.com/kyleconroy/sqlc/internal/multierr"
18-
"github.com/kyleconroy/sqlc/internal/pg"
19-
"github.com/kyleconroy/sqlc/internal/postgresql"
2015
"github.com/kyleconroy/sqlc/internal/sql/ast"
2116
"github.com/kyleconroy/sqlc/internal/sql/catalog"
2217
"github.com/kyleconroy/sqlc/internal/sql/sqlerr"
2318
"github.com/kyleconroy/sqlc/internal/sql/sqlpath"
24-
"github.com/kyleconroy/sqlc/internal/sqlite"
2519
)
2620

2721
type Parser interface {
2822
Parse(io.Reader) ([]ast.Statement, error)
23+
CommentSyntax() metadata.CommentSyntax
2924
}
3025

3126
// copied over from gen.go
@@ -145,82 +140,3 @@ func parseQueries(p Parser, c *catalog.Catalog, queries []string) (*Result, erro
145140
Queries: q,
146141
}, nil
147142
}
148-
149-
// Deprecated.
150-
func buildResult(c *catalog.Catalog) (*BuildResult, error) {
151-
var structs []golang.Struct
152-
var enums []golang.Enum
153-
for _, schema := range c.Schemas {
154-
for _, table := range schema.Tables {
155-
s := golang.Struct{
156-
Table: pg.FQN{Schema: schema.Name, Rel: table.Rel.Name},
157-
Name: strings.Title(table.Rel.Name),
158-
Comment: table.Comment,
159-
}
160-
for _, col := range table.Columns {
161-
s.Fields = append(s.Fields, golang.Field{
162-
Name: structName(col.Name),
163-
Type: "string",
164-
Tags: map[string]string{"json:": col.Name},
165-
Comment: col.Comment,
166-
})
167-
}
168-
structs = append(structs, s)
169-
}
170-
for _, typ := range schema.Types {
171-
switch t := typ.(type) {
172-
case *catalog.Enum:
173-
var name string
174-
if schema.Name == c.DefaultSchema {
175-
name = t.Name
176-
} else {
177-
name = schema.Name + "_" + t.Name
178-
}
179-
e := golang.Enum{
180-
Name: structName(name),
181-
Comment: t.Comment,
182-
}
183-
for _, v := range t.Vals {
184-
e.Constants = append(e.Constants, golang.Constant{
185-
Name: e.Name + enumValueName(v),
186-
Value: v,
187-
Type: e.Name,
188-
})
189-
}
190-
enums = append(enums, e)
191-
}
192-
}
193-
}
194-
if len(structs) > 0 {
195-
sort.Slice(structs, func(i, j int) bool { return structs[i].Name < structs[j].Name })
196-
}
197-
if len(enums) > 0 {
198-
sort.Slice(enums, func(i, j int) bool { return enums[i].Name < enums[j].Name })
199-
}
200-
return &BuildResult{structs: structs, enums: enums}, nil
201-
}
202-
203-
func Run(conf config.SQL, combo config.CombinedSettings) (*BuildResult, error) {
204-
var c *catalog.Catalog
205-
var p Parser
206-
207-
switch conf.Engine {
208-
case config.EngineXLemon:
209-
p = sqlite.NewParser()
210-
c = catalog.New("main")
211-
case config.EngineXDolphin:
212-
p = dolphin.NewParser()
213-
c = catalog.New("public") // TODO: What is the default database for MySQL?
214-
case config.EngineXElephant:
215-
p = postgresql.NewParser()
216-
c = postgresql.NewCatalog()
217-
default:
218-
return nil, fmt.Errorf("unknown engine: %s", conf.Engine)
219-
}
220-
221-
if err := parseCatalog(p, c, conf.Schema); err != nil {
222-
return nil, err
223-
}
224-
225-
return buildResult(c)
226-
}

internal/compiler/engine.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ func NewEngine(conf config.SQL, combo config.CombinedSettings) *Engine {
2828
case config.EngineXLemon:
2929
e.parser = sqlite.NewParser()
3030
e.catalog = catalog.New("main")
31-
case config.EngineXDolphin:
31+
case config.EngineMySQL, config.EngineXDolphin:
3232
e.parser = dolphin.NewParser()
3333
e.catalog = catalog.New("public") // TODO: What is the default database for MySQL?
34-
case config.EngineXElephant, config.EnginePostgreSQL:
34+
case config.EnginePostgreSQL:
3535
e.parser = postgresql.NewParser()
3636
e.catalog = postgresql.NewCatalog()
3737
default:

internal/compiler/parse.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func parseQuery(p Parser, c *catalog.Catalog, stmt ast.Node, src string, rewrite
5252
if err := validate.FuncCall(c, raw); err != nil {
5353
return nil, err
5454
}
55-
name, cmd, err := metadata.Parse(strings.TrimSpace(rawSQL), metadata.CommentSyntaxDash)
55+
name, cmd, err := metadata.Parse(strings.TrimSpace(rawSQL), p.CommentSyntax())
5656
if err != nil {
5757
return nil, err
5858
}

internal/config/config.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,8 @@ const (
7474
EnginePostgreSQL Engine = "postgresql"
7575

7676
// Experimental engines
77-
EngineXLemon Engine = "_lemon"
78-
EngineXDolphin Engine = "_dolphin"
79-
EngineXElephant Engine = "_elephant"
77+
EngineXLemon Engine = "_lemon"
78+
EngineXDolphin Engine = "_dolphin"
8079
)
8180

8281
type Config struct {

internal/dolphin/convert.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package dolphin
2+
3+
import (
4+
pcast "github.com/pingcap/parser/ast"
5+
"github.com/pingcap/parser/types"
6+
7+
"github.com/kyleconroy/sqlc/internal/sql/ast"
8+
"github.com/kyleconroy/sqlc/internal/sql/ast/pg"
9+
)
10+
11+
func convertAlterTableStmt(n *pcast.AlterTableStmt) ast.Node {
12+
alt := &ast.AlterTableStmt{
13+
Table: parseTableName(n.Table),
14+
Cmds: &ast.List{},
15+
}
16+
for _, spec := range n.Specs {
17+
switch spec.Tp {
18+
case pcast.AlterTableAddColumns:
19+
for _, def := range spec.NewColumns {
20+
name := def.Name.String()
21+
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
22+
Name: &name,
23+
Subtype: ast.AT_AddColumn,
24+
Def: &ast.ColumnDef{
25+
Colname: def.Name.String(),
26+
TypeName: &ast.TypeName{Name: types.TypeStr(def.Tp.Tp)},
27+
IsNotNull: isNotNull(def),
28+
},
29+
})
30+
}
31+
32+
case pcast.AlterTableDropColumn:
33+
name := spec.OldColumnName.String()
34+
alt.Cmds.Items = append(alt.Cmds.Items, &ast.AlterTableCmd{
35+
Name: &name,
36+
Subtype: ast.AT_DropColumn,
37+
// MissingOk: spec.IfExists,
38+
})
39+
40+
case pcast.AlterTableChangeColumn:
41+
// spew.Dump("change column", spec)
42+
43+
case pcast.AlterTableModifyColumn:
44+
// spew.Dump("modify column", spec)
45+
46+
case pcast.AlterTableAlterColumn:
47+
// spew.Dump("alter column", spec)
48+
49+
case pcast.AlterTableAddConstraint:
50+
// spew.Dump("add const", spec)
51+
52+
default:
53+
continue
54+
}
55+
}
56+
return alt
57+
}
58+
59+
func convertCreateTableStmt(n *pcast.CreateTableStmt) ast.Node {
60+
create := &ast.CreateTableStmt{
61+
Name: parseTableName(n.Table),
62+
IfNotExists: n.IfNotExists,
63+
}
64+
for _, def := range n.Cols {
65+
create.Cols = append(create.Cols, &ast.ColumnDef{
66+
Colname: def.Name.String(),
67+
TypeName: &ast.TypeName{Name: types.TypeStr(def.Tp.Tp)},
68+
IsNotNull: isNotNull(def),
69+
})
70+
}
71+
return create
72+
}
73+
74+
func convertDropTableStmt(n *pcast.DropTableStmt) ast.Node {
75+
drop := &ast.DropTableStmt{IfExists: n.IfExists}
76+
for _, name := range n.Tables {
77+
drop.Tables = append(drop.Tables, parseTableName(name))
78+
}
79+
return drop
80+
}
81+
82+
func convertSelectStmt(n *pcast.SelectStmt) ast.Node {
83+
var tables []ast.Node
84+
visit(n.From, func(n pcast.Node) {
85+
name, ok := n.(*pcast.TableName)
86+
if !ok {
87+
return
88+
}
89+
tables = append(tables, parseTableName(name))
90+
})
91+
var cols []ast.Node
92+
visit(n.Fields, func(n pcast.Node) {
93+
col, ok := n.(*pcast.ColumnName)
94+
if !ok {
95+
return
96+
}
97+
cols = append(cols, &ast.ResTarget{
98+
Val: &ast.ColumnRef{
99+
Name: col.Name.String(),
100+
},
101+
})
102+
})
103+
return &pg.SelectStmt{
104+
FromClause: &ast.List{Items: tables},
105+
TargetList: &ast.List{Items: cols},
106+
}
107+
}
108+
109+
func convert(node pcast.Node) ast.Node {
110+
switch n := node.(type) {
111+
112+
case *pcast.AlterTableStmt:
113+
return convertAlterTableStmt(n)
114+
115+
case *pcast.CreateTableStmt:
116+
return convertCreateTableStmt(n)
117+
118+
case *pcast.DropTableStmt:
119+
return convertDropTableStmt(n)
120+
121+
case *pcast.SelectStmt:
122+
return convertSelectStmt(n)
123+
124+
default:
125+
return &ast.TODO{}
126+
}
127+
}

0 commit comments

Comments
 (0)