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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy