Skip to content

Commit 7bddb83

Browse files
committed
dolphin: Add experimental parser for MySQL
1 parent 95b35d8 commit 7bddb83

File tree

1 file changed

+163
-0
lines changed

1 file changed

+163
-0
lines changed

internal/dolphin/parse.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package dolphin
2+
3+
import (
4+
"io"
5+
"io/ioutil"
6+
7+
"github.com/kyleconroy/sqlc/internal/sql/ast"
8+
9+
"github.com/pingcap/parser"
10+
pcast "github.com/pingcap/parser/ast"
11+
_ "github.com/pingcap/tidb/types/parser_driver"
12+
)
13+
14+
func NewParser() *Parser {
15+
return &Parser{parser.New()}
16+
17+
}
18+
19+
type Parser struct {
20+
pingcap *parser.Parser
21+
}
22+
23+
type nodeSearch struct {
24+
list []pcast.Node
25+
check func(pcast.Node) bool
26+
}
27+
28+
func (s *nodeSearch) Enter(n pcast.Node) (pcast.Node, bool) {
29+
if s.check(n) {
30+
s.list = append(s.list, n)
31+
}
32+
return n, false // skipChildren
33+
}
34+
35+
func (s *nodeSearch) Leave(n pcast.Node) (pcast.Node, bool) {
36+
return n, true // ok
37+
}
38+
39+
func collect(root pcast.Node, f func(pcast.Node) bool) []pcast.Node {
40+
if root == nil {
41+
return nil
42+
}
43+
ns := &nodeSearch{check: f}
44+
root.Accept(ns)
45+
return ns.list
46+
}
47+
48+
type nodeVisit struct {
49+
fn func(pcast.Node)
50+
}
51+
52+
func (s *nodeVisit) Enter(n pcast.Node) (pcast.Node, bool) {
53+
s.fn(n)
54+
return n, false // skipChildren
55+
}
56+
57+
func (s *nodeVisit) Leave(n pcast.Node) (pcast.Node, bool) {
58+
return n, true // ok
59+
}
60+
61+
func visit(root pcast.Node, f func(pcast.Node)) {
62+
if root == nil {
63+
return
64+
}
65+
ns := &nodeVisit{fn: f}
66+
root.Accept(ns)
67+
}
68+
69+
// Maybe not useful?
70+
func text(nodes []pcast.Node) []string {
71+
str := make([]string, len(nodes))
72+
for i := range nodes {
73+
if nodes[i] == nil {
74+
continue
75+
}
76+
str[i] = nodes[i].Text()
77+
}
78+
return str
79+
}
80+
81+
func (p *Parser) Parse(r io.Reader) ([]ast.Statement, error) {
82+
blob, err := ioutil.ReadAll(r)
83+
if err != nil {
84+
return nil, err
85+
}
86+
stmtNodes, _, err := p.pingcap.Parse(string(blob), "", "")
87+
if err != nil {
88+
return nil, err
89+
}
90+
var stmts []ast.Statement
91+
for i := range stmtNodes {
92+
var stmt ast.Node
93+
switch n := stmtNodes[i].(type) {
94+
95+
case *pcast.CreateTableStmt:
96+
create := &ast.CreateTableStmt{
97+
Name: &ast.TableName{
98+
Schema: n.Table.Schema.String(),
99+
Name: n.Table.Name.String(),
100+
},
101+
IfNotExists: n.IfNotExists,
102+
}
103+
for _, def := range n.Cols {
104+
create.Cols = append(create.Cols, &ast.ColumnDef{
105+
Colname: def.Name.String(),
106+
// TODO: Use n.Tp to generate type name
107+
TypeName: &ast.TypeName{Name: "text"},
108+
})
109+
}
110+
stmt = create
111+
112+
case *pcast.DropTableStmt:
113+
drop := &ast.DropTableStmt{IfExists: n.IfExists}
114+
for _, name := range n.Tables {
115+
drop.Tables = append(drop.Tables, &ast.TableName{
116+
Schema: name.Schema.String(),
117+
Name: name.Name.String(),
118+
})
119+
}
120+
stmt = drop
121+
122+
case *pcast.SelectStmt:
123+
sel := &ast.SelectStmt{}
124+
var tables []ast.Node
125+
visit(n.From, func(n pcast.Node) {
126+
name, ok := n.(*pcast.TableName)
127+
if !ok {
128+
return
129+
}
130+
tables = append(tables, &ast.TableName{
131+
Schema: name.Schema.String(),
132+
Name: name.Name.String(),
133+
})
134+
})
135+
var cols []ast.Node
136+
visit(n.Fields, func(n pcast.Node) {
137+
col, ok := n.(*pcast.ColumnName)
138+
if !ok {
139+
return
140+
}
141+
cols = append(cols, &ast.ResTarget{
142+
Val: &ast.ColumnRef{
143+
Name: col.Name.String(),
144+
},
145+
})
146+
})
147+
sel.From = &ast.List{Items: tables}
148+
sel.Fields = &ast.List{Items: cols}
149+
stmt = sel
150+
151+
default:
152+
// spew.Dump(n)
153+
154+
}
155+
156+
if stmt != nil {
157+
stmts = append(stmts, ast.Statement{
158+
Raw: &ast.RawStmt{Stmt: stmt},
159+
})
160+
}
161+
}
162+
return stmts, nil
163+
}

0 commit comments

Comments
 (0)