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

Commit 6f000e2

Browse files
authored
Merge pull request #845 from liquidata-inc/ld-master
Drop / create table
2 parents 821af8a + b011a56 commit 6f000e2

File tree

10 files changed

+390
-38
lines changed

10 files changed

+390
-38
lines changed

engine_test.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2443,7 +2443,7 @@ func TestAmbiguousColumnResolution(t *testing.T) {
24432443
require.Equal(expected, rs)
24442444
}
24452445

2446-
func TestDDL(t *testing.T) {
2446+
func TestCreateTable(t *testing.T) {
24472447
require := require.New(t)
24482448

24492449
e := newEngine(t)
@@ -2474,6 +2474,83 @@ func TestDDL(t *testing.T) {
24742474
}
24752475

24762476
require.Equal(s, testTable.Schema())
2477+
2478+
testQuery(t, e,
2479+
"CREATE TABLE t2 (a INTEGER NOT NULL PRIMARY KEY, "+
2480+
"b VARCHAR(10) NOT NULL)",
2481+
[]sql.Row(nil),
2482+
)
2483+
2484+
db, err = e.Catalog.Database("mydb")
2485+
require.NoError(err)
2486+
2487+
testTable, ok = db.Tables()["t2"]
2488+
require.True(ok)
2489+
2490+
s = sql.Schema{
2491+
{Name: "a", Type: sql.Int32, Nullable: false, PrimaryKey: true, Source: "t2"},
2492+
{Name: "b", Type: sql.Text, Nullable: false, Source: "t2"},
2493+
}
2494+
2495+
require.Equal(s, testTable.Schema())
2496+
2497+
testQuery(t, e,
2498+
"CREATE TABLE t3(a INTEGER NOT NULL,"+
2499+
"b TEXT NOT NULL,"+
2500+
"c bool, primary key (a,b))",
2501+
[]sql.Row(nil),
2502+
)
2503+
2504+
db, err = e.Catalog.Database("mydb")
2505+
require.NoError(err)
2506+
2507+
testTable, ok = db.Tables()["t3"]
2508+
require.True(ok)
2509+
2510+
s = sql.Schema{
2511+
{Name: "a", Type: sql.Int32, Nullable: false, PrimaryKey: true, Source: "t3"},
2512+
{Name: "b", Type: sql.Text, Nullable: false, PrimaryKey: true, Source: "t3"},
2513+
{Name: "c", Type: sql.Uint8, Nullable: true, Source: "t3"},
2514+
}
2515+
2516+
require.Equal(s, testTable.Schema())
2517+
}
2518+
2519+
func TestDropTable(t *testing.T) {
2520+
require := require.New(t)
2521+
2522+
e := newEngine(t)
2523+
db, err := e.Catalog.Database("mydb")
2524+
require.NoError(err)
2525+
2526+
_, ok := db.Tables()["mytable"]
2527+
require.True(ok)
2528+
2529+
testQuery(t, e,
2530+
"DROP TABLE IF EXISTS mytable, not_exist",
2531+
[]sql.Row(nil),
2532+
)
2533+
2534+
_, ok = db.Tables()["mytable"]
2535+
require.False(ok)
2536+
2537+
_, ok = db.Tables()["othertable"]
2538+
require.True(ok)
2539+
_, ok = db.Tables()["tabletest"]
2540+
require.True(ok)
2541+
2542+
testQuery(t, e,
2543+
"DROP TABLE IF EXISTS othertable, tabletest",
2544+
[]sql.Row(nil),
2545+
)
2546+
2547+
_, ok = db.Tables()["othertable"]
2548+
require.False(ok)
2549+
_, ok = db.Tables()["tabletest"]
2550+
require.False(ok)
2551+
2552+
_, _, err = e.Query(newCtx(), "DROP TABLE not_exist")
2553+
require.Error(err)
24772554
}
24782555

24792556
func TestNaturalJoin(t *testing.T) {

memory/database.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ func (d *Database) AddTable(name string, t sql.Table) {
3333
d.tables[name] = t
3434
}
3535

36-
// Create creates a table with the given name and schema
37-
func (d *Database) Create(name string, schema sql.Schema) error {
36+
// CreateTable creates a table with the given name and schema
37+
func (d *Database) CreateTable(ctx *sql.Context, name string, schema sql.Schema) error {
3838
_, ok := d.tables[name]
3939
if ok {
4040
return sql.ErrTableAlreadyExists.New(name)
@@ -43,3 +43,15 @@ func (d *Database) Create(name string, schema sql.Schema) error {
4343
d.tables[name] = NewTable(name, schema)
4444
return nil
4545
}
46+
47+
// DropTable drops the table with the given name
48+
func (d *Database) DropTable(ctx *sql.Context, name string) error {
49+
_, ok := d.tables[name]
50+
if !ok {
51+
return sql.ErrTableNotFound.New(name)
52+
}
53+
54+
delete(d.tables, name)
55+
return nil
56+
}
57+

memory/database_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ func TestDatabase_AddTable(t *testing.T) {
1919
tables := db.Tables()
2020
require.Equal(0, len(tables))
2121

22-
var altDb sql.Alterable = db
23-
err := altDb.Create("test_table", nil)
22+
err := db.CreateTable(sql.NewEmptyContext(), "test_table", nil)
2423
require.NoError(err)
2524

2625
tables = db.Tables()
@@ -29,6 +28,6 @@ func TestDatabase_AddTable(t *testing.T) {
2928
require.True(ok)
3029
require.NotNil(tt)
3130

32-
err = altDb.Create("test_table", nil)
31+
err = db.CreateTable(sql.NewEmptyContext(), "test_table", nil)
3332
require.Error(err)
3433
}

sql/core.go

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

233-
// Alterable should be implemented by databases that can handle DDL statements
234-
type Alterable interface {
235-
Create(name string, schema Schema) error
233+
// TableCreator should be implemented by databases that can create new tables.
234+
type TableCreator interface {
235+
CreateTable(ctx *Context, name string, schema Schema) error
236+
}
237+
238+
// TableDropper should be implemented by databases that can drop tables.
239+
type TableDropper interface {
240+
DropTable(ctx *Context, name string) error
236241
}
237242

238243
// Lockable should be implemented by tables that can be locked and unlocked.

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) {

0 commit comments

Comments
 (0)