Skip to content

Commit 1d2916b

Browse files
authored
Support ALTER TABLE for SQLite (#414)
* sqlite/parser: Add support for renaming columns Renaming columns was added in version 3.25.0 https://sqlite.org/releaselog/3_25_0.html * sqlite: Implement support for ALTER TABLE * sqlite/parser: Remove generated visitor code The generated Visitor code does not work, so just get rid of it.
1 parent 2ad9a8e commit 1d2916b

File tree

11 files changed

+2505
-3441
lines changed

11 files changed

+2505
-3441
lines changed

internal/sqlite/catalog.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package sqlite
2+
3+
import "github.com/kyleconroy/sqlc/internal/sql/catalog"
4+
5+
func NewCatalog() *catalog.Catalog {
6+
c := catalog.New("main")
7+
return c
8+
}

internal/sqlite/catalog_test.go

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
package sqlite
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
"testing"
7+
8+
"github.com/kyleconroy/sqlc/internal/sql/ast"
9+
"github.com/kyleconroy/sqlc/internal/sql/catalog"
10+
11+
"github.com/google/go-cmp/cmp"
12+
"github.com/google/go-cmp/cmp/cmpopts"
13+
)
14+
15+
func TestUpdate(t *testing.T) {
16+
p := NewParser()
17+
18+
for i, tc := range []struct {
19+
stmt string
20+
s *catalog.Schema
21+
}{
22+
{
23+
`
24+
CREATE TABLE foo (bar text);
25+
`,
26+
&catalog.Schema{
27+
Name: "main",
28+
Tables: []*catalog.Table{
29+
{
30+
Rel: &ast.TableName{Name: "foo"},
31+
Columns: []*catalog.Column{
32+
{
33+
Name: "bar",
34+
Type: ast.TypeName{Name: "text"},
35+
},
36+
},
37+
},
38+
},
39+
},
40+
},
41+
{
42+
`
43+
CREATE TABLE foo (bar text);
44+
ALTER TABLE foo RENAME TO baz;
45+
`,
46+
&catalog.Schema{
47+
Name: "main",
48+
Tables: []*catalog.Table{
49+
{
50+
Rel: &ast.TableName{Name: "baz"},
51+
Columns: []*catalog.Column{
52+
{
53+
Name: "bar",
54+
Type: ast.TypeName{Name: "text"},
55+
},
56+
},
57+
},
58+
},
59+
},
60+
},
61+
{
62+
`
63+
CREATE TABLE foo (bar text);
64+
ALTER TABLE foo ADD COLUMN baz bool;
65+
`,
66+
&catalog.Schema{
67+
Name: "main",
68+
Tables: []*catalog.Table{
69+
{
70+
Rel: &ast.TableName{Name: "foo"},
71+
Columns: []*catalog.Column{
72+
{
73+
Name: "bar",
74+
Type: ast.TypeName{Name: "text"},
75+
},
76+
{
77+
Name: "baz",
78+
Type: ast.TypeName{Name: "bool"},
79+
},
80+
},
81+
},
82+
},
83+
},
84+
},
85+
{
86+
`
87+
CREATE TABLE foo (bar text);
88+
ALTER TABLE foo RENAME COLUMN bar TO baz;
89+
`,
90+
&catalog.Schema{
91+
Name: "main",
92+
Tables: []*catalog.Table{
93+
{
94+
Rel: &ast.TableName{Name: "foo"},
95+
Columns: []*catalog.Column{
96+
{
97+
Name: "baz",
98+
Type: ast.TypeName{Name: "text"},
99+
},
100+
},
101+
},
102+
},
103+
},
104+
},
105+
{
106+
`
107+
CREATE TABLE foo (bar text);
108+
ALTER TABLE foo RENAME bar TO baz;
109+
`,
110+
&catalog.Schema{
111+
Name: "main",
112+
Tables: []*catalog.Table{
113+
{
114+
Rel: &ast.TableName{Name: "foo"},
115+
Columns: []*catalog.Column{
116+
{
117+
Name: "baz",
118+
Type: ast.TypeName{Name: "text"},
119+
},
120+
},
121+
},
122+
},
123+
},
124+
},
125+
{
126+
`
127+
ATTACH ':memory:' as ns;
128+
CREATE TABLE ns.foo (bar text);
129+
`,
130+
&catalog.Schema{
131+
Name: "ns",
132+
Tables: []*catalog.Table{
133+
{
134+
Rel: &ast.TableName{Schema: "ns", Name: "foo"},
135+
Columns: []*catalog.Column{
136+
{
137+
Name: "bar",
138+
Type: ast.TypeName{Name: "text"},
139+
},
140+
},
141+
},
142+
},
143+
},
144+
},
145+
{
146+
`
147+
ATTACH ':memory:' as ns;
148+
CREATE TABLE ns.foo (bar text);
149+
ALTER TABLE ns.foo RENAME TO baz;
150+
`,
151+
&catalog.Schema{
152+
Name: "ns",
153+
Tables: []*catalog.Table{
154+
{
155+
Rel: &ast.TableName{Schema: "ns", Name: "baz"},
156+
Columns: []*catalog.Column{
157+
{
158+
Name: "bar",
159+
Type: ast.TypeName{Name: "text"},
160+
},
161+
},
162+
},
163+
},
164+
},
165+
},
166+
{
167+
`
168+
ATTACH ':memory:' as ns;
169+
CREATE TABLE ns.foo (bar text);
170+
ALTER TABLE ns.foo ADD COLUMN baz bool;
171+
`,
172+
&catalog.Schema{
173+
Name: "ns",
174+
Tables: []*catalog.Table{
175+
{
176+
Rel: &ast.TableName{Schema: "ns", Name: "foo"},
177+
Columns: []*catalog.Column{
178+
{
179+
Name: "bar",
180+
Type: ast.TypeName{Name: "text"},
181+
},
182+
{
183+
Name: "baz",
184+
Type: ast.TypeName{Name: "bool"},
185+
},
186+
},
187+
},
188+
},
189+
},
190+
},
191+
{
192+
`
193+
ATTACH ':memory:' as ns;
194+
CREATE TABLE ns.foo (bar text);
195+
ALTER TABLE ns.foo RENAME COLUMN bar TO baz;
196+
`,
197+
&catalog.Schema{
198+
Name: "ns",
199+
Tables: []*catalog.Table{
200+
{
201+
Rel: &ast.TableName{Schema: "ns", Name: "foo"},
202+
Columns: []*catalog.Column{
203+
{
204+
Name: "baz",
205+
Type: ast.TypeName{Name: "text"},
206+
},
207+
},
208+
},
209+
},
210+
},
211+
},
212+
{
213+
`
214+
ATTACH ':memory:' as ns;
215+
CREATE TABLE ns.foo (bar text);
216+
ALTER TABLE ns.foo RENAME bar TO baz;
217+
`,
218+
&catalog.Schema{
219+
Name: "ns",
220+
Tables: []*catalog.Table{
221+
{
222+
Rel: &ast.TableName{Schema: "ns", Name: "foo"},
223+
Columns: []*catalog.Column{
224+
{
225+
Name: "baz",
226+
Type: ast.TypeName{Name: "text"},
227+
},
228+
},
229+
},
230+
},
231+
},
232+
},
233+
} {
234+
test := tc
235+
t.Run(strconv.Itoa(i), func(t *testing.T) {
236+
stmts, err := p.Parse(strings.NewReader(test.stmt))
237+
if err != nil {
238+
t.Log(test.stmt)
239+
t.Fatal(err)
240+
}
241+
242+
c := NewCatalog()
243+
if err := c.Build(stmts); err != nil {
244+
t.Log(test.stmt)
245+
t.Fatal(err)
246+
}
247+
248+
e := NewCatalog()
249+
if test.s != nil {
250+
var replaced bool
251+
for i := range e.Schemas {
252+
if e.Schemas[i].Name == test.s.Name {
253+
e.Schemas[i] = test.s
254+
replaced = true
255+
break
256+
}
257+
}
258+
if !replaced {
259+
e.Schemas = append(e.Schemas, test.s)
260+
}
261+
}
262+
263+
if diff := cmp.Diff(e, c, cmpopts.EquateEmpty()); diff != "" {
264+
t.Log(test.stmt)
265+
t.Errorf("catalog mismatch:\n%s", diff)
266+
}
267+
})
268+
}
269+
}

internal/sqlite/listener.go

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,34 @@ func (l *listener) EnterAlter_table_stmt(c *parser.Alter_table_stmtContext) {
3939
return
4040
}
4141

42-
stmt := &ast.AlterTableStmt{
43-
Table: parseTableName(c),
44-
Cmds: &ast.List{},
42+
if newTable, ok := c.New_table_name().(*parser.New_table_nameContext); ok {
43+
name := newTable.Any_name().GetText()
44+
stmt := &ast.RenameTableStmt{
45+
Table: parseTableName(c),
46+
NewName: &name,
47+
}
48+
l.stmt = &ast.RawStmt{Stmt: stmt}
49+
return
50+
}
51+
52+
if newCol, ok := c.New_column_name().(*parser.New_column_nameContext); ok {
53+
name := newCol.Any_name().GetText()
54+
stmt := &ast.RenameColumnStmt{
55+
Table: parseTableName(c),
56+
Col: &ast.ColumnRef{
57+
Name: c.Column_name().GetText(),
58+
},
59+
NewName: &name,
60+
}
61+
l.stmt = &ast.RawStmt{Stmt: stmt}
62+
return
4563
}
4664

4765
if def, ok := c.Column_def().(*parser.Column_defContext); ok {
66+
stmt := &ast.AlterTableStmt{
67+
Table: parseTableName(c),
68+
Cmds: &ast.List{},
69+
}
4870
name := def.Column_name().GetText()
4971
stmt.Cmds.Items = append(stmt.Cmds.Items, &ast.AlterTableCmd{
5072
Name: &name,
@@ -56,10 +78,23 @@ func (l *listener) EnterAlter_table_stmt(c *parser.Alter_table_stmtContext) {
5678
},
5779
},
5880
})
81+
l.stmt = &ast.RawStmt{Stmt: stmt}
82+
return
5983
}
6084

61-
l.stmt = &ast.RawStmt{Stmt: stmt}
85+
}
86+
87+
func (l *listener) EnterAttach_stmt(c *parser.Attach_stmtContext) {
88+
if l.busy() {
89+
return
90+
}
6291

92+
name := c.Database_name().GetText()
93+
stmt := &ast.CreateSchemaStmt{
94+
Name: &name,
95+
}
96+
97+
l.stmt = &ast.RawStmt{Stmt: stmt}
6398
}
6499

65100
func (l *listener) EnterCreate_table_stmt(c *parser.Create_table_stmtContext) {
@@ -149,3 +184,5 @@ func (l *listener) EnterFactored_select_stmt(c *parser.Factored_select_stmtConte
149184
}
150185
l.stmt = &ast.RawStmt{Stmt: sel}
151186
}
187+
188+
var _ parser.SQLiteListener = (*listener)(nil)

internal/sqlite/parser/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
sqlite_parser.go: SQLite.g4
2-
antlr -Dlanguage=Go -visitor SQLite.g4
2+
antlr -Dlanguage=Go SQLite.g4

internal/sqlite/parser/SQLite.g4

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ sql_stmt
7474
alter_table_stmt
7575
: K_ALTER K_TABLE ( database_name '.' )? table_name
7676
( K_RENAME K_TO new_table_name
77+
| K_RENAME K_COLUMN? column_name K_TO new_column_name
7778
| K_ADD K_COLUMN? column_def
7879
)
7980
;
@@ -633,6 +634,10 @@ column_name
633634
: any_name
634635
;
635636

637+
new_column_name
638+
: any_name
639+
;
640+
636641
collation_name
637642
: any_name
638643
;

internal/sqlite/parser/SQLite.interp

Lines changed: 2 additions & 1 deletion
Large diffs are not rendered by default.

internal/sqlite/parser/sqlite_base_listener.go

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)