Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

Commit 4b9329e

Browse files
authored
Merge pull request #18 from liquidata-inc/zachmu/drop-table
Drop and create table support, as well as support for 24-bit integers.
2 parents 89e8320 + 808d3ef commit 4b9329e

File tree

8 files changed

+322
-24
lines changed

8 files changed

+322
-24
lines changed

memory/database.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,25 @@ func (d *Database) Create(name string, schema sql.Schema) error {
4343
d.tables[name] = NewTable(name, schema)
4444
return nil
4545
}
46+
47+
// Create creates a table with the given name and schema
48+
func (d *Database) CreateTable(ctx *sql.Context, name string, schema sql.Schema) error {
49+
_, ok := d.tables[name]
50+
if ok {
51+
return sql.ErrTableAlreadyExists.New(name)
52+
}
53+
54+
d.tables[name] = NewTable(name, schema)
55+
return nil
56+
}
57+
58+
func (d *Database) DropTable(ctx *sql.Context, name string) error {
59+
_, ok := d.tables[name]
60+
if !ok {
61+
return sql.ErrTableNotFound.New(name)
62+
}
63+
64+
delete(d.tables, name)
65+
return nil
66+
}
67+

sql/core.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,22 @@ type Database interface {
230230
Tables() map[string]Table
231231
}
232232

233+
// DEPRECATED. Use TableCreator and TableDropper.
233234
// Alterable should be implemented by databases that can handle DDL statements
234235
type Alterable interface {
235236
Create(name string, schema Schema) error
236237
}
237238

239+
// TableCreator should be implemented by databases that can create new tables.
240+
type TableCreator interface {
241+
CreateTable(ctx *Context, name string, schema Schema) error
242+
}
243+
244+
// TableDropper should be implemented by databases that can drop tables.
245+
type TableDropper interface {
246+
DropTable(ctx *Context, name string) error
247+
}
248+
238249
// Lockable should be implemented by tables that can be locked and unlocked.
239250
type Lockable interface {
240251
Nameable

sql/parse/parse.go

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ var (
4747
unlockTablesRegex = regexp.MustCompile(`^unlock\s+tables$`)
4848
lockTablesRegex = regexp.MustCompile(`^lock\s+tables\s`)
4949
setRegex = regexp.MustCompile(`^set\s+`)
50+
createViewRegex = regexp.MustCompile(`^create\s+view\s+`)
51+
)
52+
53+
// These constants aren't exported from vitess for some reason. This could be removed if we changed this.
54+
const (
55+
colKeyNone sqlparser.ColumnKeyOption = iota
56+
colKeyPrimary
57+
colKeySpatialKey
58+
colKeyUnique
59+
colKeyUniqueKey
60+
colKey
5061
)
5162

5263
// Parse parses the given SQL sentence and returns the corresponding node.
@@ -93,6 +104,9 @@ func Parse(ctx *sql.Context, query string) (sql.Node, error) {
93104
return parseLockTables(ctx, s)
94105
case setRegex.MatchString(lowerQuery):
95106
s = fixSetQuery(s)
107+
case createViewRegex.MatchString(lowerQuery):
108+
// CREATE VIEW parses as a CREATE DDL statement with an empty table spec
109+
return nil, ErrUnsupportedFeature.New("CREATE VIEW")
96110
}
97111

98112
stmt, err := sqlparser.Parse(s)
@@ -144,7 +158,12 @@ func convert(ctx *sql.Context, stmt sqlparser.Statement, query string) (sql.Node
144158
case *sqlparser.Insert:
145159
return convertInsert(ctx, n)
146160
case *sqlparser.DDL:
147-
return convertDDL(n)
161+
// unlike other statements, DDL statements have loose parsing by default
162+
ddl, err := sqlparser.ParseStrictDDL(query)
163+
if err != nil {
164+
return nil, err
165+
}
166+
return convertDDL(ddl.(*sqlparser.DDL))
148167
case *sqlparser.Set:
149168
return convertSet(ctx, n)
150169
case *sqlparser.Use:
@@ -354,13 +373,23 @@ func convertDDL(c *sqlparser.DDL) (sql.Node, error) {
354373
switch c.Action {
355374
case sqlparser.CreateStr:
356375
return convertCreateTable(c)
376+
case sqlparser.DropStr:
377+
return convertDropTable(c)
357378
default:
358379
return nil, ErrUnsupportedSyntax.New(c)
359380
}
360381
}
361382

383+
func convertDropTable(c *sqlparser.DDL) (sql.Node, error) {
384+
tableNames := make([]string, len(c.FromTables))
385+
for i, t := range c.FromTables {
386+
tableNames[i] = t.Name.String()
387+
}
388+
return plan.NewDropTable(sql.UnresolvedDatabase(""), c.IfExists, tableNames...), nil
389+
}
390+
362391
func convertCreateTable(c *sqlparser.DDL) (sql.Node, error) {
363-
schema, err := columnDefinitionToSchema(c.TableSpec.Columns)
392+
schema, err := tableSpecToSchema(c.TableSpec)
364393
if err != nil {
365394
return nil, err
366395
}
@@ -462,6 +491,7 @@ func convertUpdate(ctx *sql.Context, d *sqlparser.Update) (sql.Node, error) {
462491
if err != nil {
463492
return nil, err
464493
}
494+
465495
}
466496

467497
if d.Limit != nil {
@@ -474,27 +504,56 @@ func convertUpdate(ctx *sql.Context, d *sqlparser.Update) (sql.Node, error) {
474504
return plan.NewUpdate(node, updateExprs), nil
475505
}
476506

477-
func columnDefinitionToSchema(colDef []*sqlparser.ColumnDefinition) (sql.Schema, error) {
507+
func tableSpecToSchema(tableSpec *sqlparser.TableSpec) (sql.Schema, error) {
478508
var schema sql.Schema
479-
for _, cd := range colDef {
480-
typ := cd.Type
481-
internalTyp, err := sql.MysqlTypeToType(typ.SQLType())
509+
for _, cd := range tableSpec.Columns {
510+
column, err := getColumn(cd, tableSpec.Indexes)
482511
if err != nil {
483512
return nil, err
484513
}
485514

486-
schema = append(schema, &sql.Column{
487-
Nullable: !bool(typ.NotNull),
488-
Type: internalTyp,
489-
Name: cd.Name.String(),
490-
// TODO
491-
Default: nil,
492-
})
515+
schema = append(schema, column)
493516
}
494517

495518
return schema, nil
496519
}
497520

521+
// getColumn returns the sql.Column for the column definition given, as part of a create table statement.
522+
func getColumn(cd *sqlparser.ColumnDefinition, indexes []*sqlparser.IndexDefinition) (*sql.Column, error) {
523+
typ := cd.Type
524+
internalTyp, err := sql.MysqlTypeToType(typ.SQLType())
525+
if err != nil {
526+
return nil, err
527+
}
528+
529+
// Primary key info can either be specified in the column's type info (for in-line declarations), or in a slice of
530+
// indexes attached to the table def. We have to check both places to find if a column is part of the primary key
531+
isPkey := cd.Type.KeyOpt == colKeyPrimary
532+
533+
if !isPkey {
534+
OuterLoop:
535+
for _, index := range indexes {
536+
if index.Info.Primary {
537+
for _, indexCol := range index.Columns {
538+
if indexCol.Column.Equal(cd.Name) {
539+
isPkey = true
540+
break OuterLoop
541+
}
542+
}
543+
}
544+
}
545+
}
546+
547+
return &sql.Column{
548+
Nullable: !bool(typ.NotNull),
549+
Type: internalTyp,
550+
Name: cd.Name.String(),
551+
PrimaryKey: isPkey,
552+
// TODO
553+
Default: nil,
554+
}, nil
555+
}
556+
498557
func columnsToStrings(cols sqlparser.Columns) []string {
499558
res := make([]string, len(cols))
500559
for i, c := range cols {

sql/parse/parse_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,60 @@ var fixtures = map[string]sql.Node{
5151
Nullable: true,
5252
}},
5353
),
54+
`CREATE TABLE t1(a INTEGER NOT NULL PRIMARY KEY, b TEXT)`: plan.NewCreateTable(
55+
sql.UnresolvedDatabase(""),
56+
"t1",
57+
sql.Schema{{
58+
Name: "a",
59+
Type: sql.Int32,
60+
Nullable: false,
61+
PrimaryKey: true,
62+
}, {
63+
Name: "b",
64+
Type: sql.Text,
65+
Nullable: true,
66+
PrimaryKey: false,
67+
}},
68+
),
69+
`CREATE TABLE t1(a INTEGER, b TEXT, PRIMARY KEY (a))`: plan.NewCreateTable(
70+
sql.UnresolvedDatabase(""),
71+
"t1",
72+
sql.Schema{{
73+
Name: "a",
74+
Type: sql.Int32,
75+
Nullable: true,
76+
PrimaryKey: true,
77+
}, {
78+
Name: "b",
79+
Type: sql.Text,
80+
Nullable: true,
81+
PrimaryKey: false,
82+
}},
83+
),
84+
`CREATE TABLE t1(a INTEGER, b TEXT, PRIMARY KEY (a, b))`: plan.NewCreateTable(
85+
sql.UnresolvedDatabase(""),
86+
"t1",
87+
sql.Schema{{
88+
Name: "a",
89+
Type: sql.Int32,
90+
Nullable: true,
91+
PrimaryKey: true,
92+
}, {
93+
Name: "b",
94+
Type: sql.Text,
95+
Nullable: true,
96+
PrimaryKey: true,
97+
}},
98+
),
99+
`DROP TABLE foo;`: plan.NewDropTable(
100+
sql.UnresolvedDatabase(""), false, "foo",
101+
),
102+
`DROP TABLE IF EXISTS foo;`: plan.NewDropTable(
103+
sql.UnresolvedDatabase(""), true, "foo",
104+
),
105+
`DROP TABLE IF EXISTS foo, bar, baz;`: plan.NewDropTable(
106+
sql.UnresolvedDatabase(""), true, "foo", "bar", "baz",
107+
),
54108
`DESCRIBE TABLE foo;`: plan.NewDescribe(
55109
plan.NewUnresolvedTable("foo", ""),
56110
),
@@ -1242,6 +1296,7 @@ var fixturesErrors = map[string]*errors.Kind{
12421296
`SELECT INTERVAL 1 DAY + INTERVAL 1 DAY`: ErrUnsupportedSyntax,
12431297
`SELECT '2018-05-01' + (INTERVAL 1 DAY + INTERVAL 1 DAY)`: ErrUnsupportedSyntax,
12441298
`SELECT AVG(DISTINCT foo) FROM b`: ErrUnsupportedSyntax,
1299+
`CREATE VIEW view1 AS SELECT x FROM t1 WHERE x>0`: ErrUnsupportedFeature,
12451300
}
12461301

12471302
func TestParseErrors(t *testing.T) {

sql/plan/ddl.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package plan
22

33
import (
4+
"fmt"
45
"github.com/src-d/go-mysql-server/sql"
56
"gopkg.in/src-d/go-errors.v1"
67
)
78

89
// ErrCreateTable is thrown when the database doesn't support table creation
910
var ErrCreateTable = errors.NewKind("tables cannot be created on database %s")
11+
var ErrDropTableNotSupported = errors.NewKind("tables cannot be dropped on database %s")
1012

1113
// CreateTable is a node describing the creation of some table.
1214
type CreateTable struct {
@@ -15,6 +17,13 @@ type CreateTable struct {
1517
schema sql.Schema
1618
}
1719

20+
// DropTable is a node describing dropping a table
21+
type DropTable struct {
22+
db sql.Database
23+
names []string
24+
ifExists bool
25+
}
26+
1827
// NewCreateTable creates a new CreateTable node
1928
func NewCreateTable(db sql.Database, name string, schema sql.Schema) *CreateTable {
2029
for _, s := range schema {
@@ -50,6 +59,12 @@ func (c *CreateTable) Resolved() bool {
5059

5160
// RowIter implements the Node interface.
5261
func (c *CreateTable) RowIter(s *sql.Context) (sql.RowIter, error) {
62+
creatable, ok := c.db.(sql.TableCreator)
63+
if ok {
64+
return sql.RowsToRowIter(), creatable.CreateTable(s, c.name, c.schema)
65+
}
66+
67+
// TODO: phase out this interface
5368
d, ok := c.db.(sql.Alterable)
5469
if !ok {
5570
return nil, ErrCreateTable.New(c.db.Name())
@@ -75,3 +90,79 @@ func (c *CreateTable) WithChildren(children ...sql.Node) (sql.Node, error) {
7590
func (c *CreateTable) String() string {
7691
return "CreateTable"
7792
}
93+
94+
// NewDropTable creates a new DropTable node
95+
func NewDropTable(db sql.Database, ifExists bool, tableNames ...string) *DropTable {
96+
return &DropTable{
97+
db: db,
98+
names: tableNames,
99+
ifExists: ifExists,
100+
}
101+
}
102+
103+
var _ sql.Databaser = (*DropTable)(nil)
104+
105+
// Database implements the sql.Databaser interface.
106+
func (d *DropTable) Database() sql.Database {
107+
return d.db
108+
}
109+
110+
// WithDatabase implements the sql.Databaser interface.
111+
func (d *DropTable) WithDatabase(db sql.Database) (sql.Node, error) {
112+
nc := *d
113+
nc.db = db
114+
return &nc, nil
115+
}
116+
117+
// Resolved implements the Resolvable interface.
118+
func (d *DropTable) Resolved() bool {
119+
_, ok := d.db.(sql.UnresolvedDatabase)
120+
return !ok
121+
}
122+
123+
// RowIter implements the Node interface.
124+
func (d *DropTable) RowIter(s *sql.Context) (sql.RowIter, error) {
125+
droppable, ok := d.db.(sql.TableDropper)
126+
if !ok {
127+
return nil, ErrDropTableNotSupported.New(d.db.Name())
128+
}
129+
130+
var err error
131+
for _, tableName := range d.names {
132+
_, ok := d.db.Tables()[tableName]
133+
if !ok {
134+
if d.ifExists {
135+
continue
136+
}
137+
return nil, sql.ErrTableNotFound.New(tableName)
138+
}
139+
err = droppable.DropTable(s, tableName)
140+
if err != nil {
141+
break
142+
}
143+
}
144+
145+
return sql.RowsToRowIter(), err
146+
}
147+
148+
// Schema implements the Node interface.
149+
func (d *DropTable) Schema() sql.Schema { return nil }
150+
151+
// Children implements the Node interface.
152+
func (d *DropTable) Children() []sql.Node { return nil }
153+
154+
// WithChildren implements the Node interface.
155+
func (d *DropTable) WithChildren(children ...sql.Node) (sql.Node, error) {
156+
if len(children) != 0 {
157+
return nil, sql.ErrInvalidChildrenNumber.New(d, len(children), 0)
158+
}
159+
return d, nil
160+
}
161+
162+
func (d *DropTable) String() string {
163+
ifExists := ""
164+
if d.ifExists {
165+
ifExists = "if exists "
166+
}
167+
return fmt.Sprintf("Drop table %s%s", ifExists, d.names)
168+
}

0 commit comments

Comments
 (0)