Skip to content

Commit c370774

Browse files
author
James Cor
committed
add if not exists option to create view
1 parent b7b74d4 commit c370774

File tree

7 files changed

+69
-19
lines changed

7 files changed

+69
-19
lines changed

enginetest/queries/view_queries.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,36 @@ import (
2020
)
2121

2222
var ViewScripts = []ScriptTest{
23+
{
24+
Name: "existing views",
25+
SetUpScript: []string{
26+
"create view v as select 1;",
27+
"create table t (i int);",
28+
"insert into t values (1);",
29+
},
30+
Assertions: []ScriptTestAssertion{
31+
{
32+
Query: "create if not exists view v as select 2;",
33+
Expected: []sql.Row{
34+
{types.NewOkResult(0)},
35+
},
36+
},
37+
{
38+
Query: "select * from v;",
39+
Expected: []sql.Row{{1}},
40+
},
41+
{
42+
Query: "create if not exists view t as select 2;",
43+
Expected: []sql.Row{
44+
{types.NewOkResult(0)},
45+
},
46+
},
47+
{
48+
Query: "select * from t;",
49+
Expected: []sql.Row{{1}},
50+
},
51+
},
52+
},
2353
{
2454
Name: "multi database view",
2555
SetUpScript: []string{

memory/database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ func (d *Database) Database() *BaseDatabase {
555555
func (d *Database) CreateView(ctx *sql.Context, name string, selectStatement, createViewStmt string) error {
556556
_, ok := d.views[strings.ToLower(name)]
557557
if ok {
558-
return sql.ErrExistingView.New(name)
558+
return sql.ErrExistingView.New(d.Name(), name)
559559
}
560560

561561
sqlMode := sql.LoadSqlMode(ctx)

sql/plan/create_view.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type CreateView struct {
3131
database sql.Database
3232
targetSchema sql.Schema
3333
Name string
34+
IfNotExists bool
3435
IsReplace bool
3536
Definition *SubqueryAlias
3637
CreateViewString string
@@ -46,11 +47,12 @@ var _ sql.SchemaTarget = (*CreateView)(nil)
4647

4748
// NewCreateView creates a CreateView node with the specified parameters,
4849
// setting its catalog to nil.
49-
func NewCreateView(database sql.Database, name string, definition *SubqueryAlias, isReplace bool, createViewStr, algorithm, definer, security string) *CreateView {
50+
func NewCreateView(database sql.Database, name string, definition *SubqueryAlias, ifNotExists, isReplace bool, createViewStr, algorithm, definer, security string) *CreateView {
5051
return &CreateView{
5152
UnaryNode: UnaryNode{Child: definition},
5253
database: database,
5354
Name: name,
55+
IfNotExists: ifNotExists,
5456
IsReplace: isReplace,
5557
Definition: definition,
5658
CreateViewString: createViewStr,

sql/planbuilder/create_ddl.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ func (b *Builder) buildCreateView(inScope *scope, subQuery string, fullQuery str
672672
if !ok {
673673
b.handleErr(sql.ErrDatabaseSchemaNotFound.New(c.Table.SchemaQualifier.String()))
674674
}
675-
createView := plan.NewCreateView(db, c.ViewSpec.ViewName.Name.String(), queryAlias, c.OrReplace, subQuery, c.ViewSpec.Algorithm, definer, c.ViewSpec.Security)
675+
createView := plan.NewCreateView(db, c.ViewSpec.ViewName.Name.String(), queryAlias, c.IfNotExists, c.OrReplace, subQuery, c.ViewSpec.Algorithm, definer, c.ViewSpec.Security)
676676
outScope.node = b.modifySchemaTarget(queryScope, createView, createView.Definition.Schema())
677677

678678
return outScope

sql/rowexec/create_view_test.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import (
2727
"github.com/dolthub/go-mysql-server/sql/types"
2828
)
2929

30-
func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView {
30+
func newCreateView(db memory.MemoryDatabase, ifNotExists, isReplace bool) *plan.CreateView {
3131
table := memory.NewTable(db.Database(), "mytable", sql.NewPrimaryKeySchema(sql.Schema{
3232
{Name: "i", Source: "mytable", Type: types.Int32},
3333
{Name: "s", Source: "mytable", Type: types.Text},
@@ -44,7 +44,7 @@ func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView {
4444
),
4545
)
4646

47-
createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, isReplace, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "")
47+
createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, ifNotExists, isReplace, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "")
4848

4949
return createView
5050
}
@@ -54,7 +54,7 @@ func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView {
5454
func TestCreateViewWithRegistry(t *testing.T) {
5555
require := require.New(t)
5656

57-
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false)
57+
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false)
5858

5959
ctx := sql.NewContext(context.Background())
6060
_, err := DefaultBuilder.buildNodeExec(ctx, createView, nil)
@@ -68,7 +68,8 @@ func TestCreateViewWithRegistry(t *testing.T) {
6868

6969
// Tests that CreateView RowIter returns an error when the view exists
7070
func TestCreateExistingViewNative(t *testing.T) {
71-
createView := newCreateView(memory.NewDatabase("mydb"), false)
71+
createView := newCreateView(memory.NewDatabase("mydb"), false, false)
72+
createExistingView := newCreateView(memory.NewDatabase("mydb"), true, false)
7273

7374
ctx := sql.NewContext(context.Background())
7475
_, err := DefaultBuilder.buildNodeExec(ctx, createView, nil)
@@ -78,13 +79,17 @@ func TestCreateExistingViewNative(t *testing.T) {
7879
_, err = DefaultBuilder.buildNodeExec(ctx, createView, nil)
7980
require.Error(t, err)
8081
require.True(t, sql.ErrExistingView.Is(err))
82+
83+
ctx = sql.NewContext(context.Background())
84+
_, err = DefaultBuilder.buildNodeExec(ctx, createExistingView, nil)
85+
require.NoError(t, err)
8186
}
8287

8388
// Tests that CreateView RowIter succeeds when the view exists and the
8489
// IsReplace flag is set to true
8590
func TestReplaceExistingViewNative(t *testing.T) {
8691
db := memory.NewDatabase("mydb")
87-
createView := newCreateView(db, false)
92+
createView := newCreateView(db, false, false)
8893

8994
ctx := sql.NewContext(context.Background())
9095
_, err := DefaultBuilder.buildNodeExec(ctx, createView, nil)
@@ -110,7 +115,7 @@ func TestReplaceExistingViewNative(t *testing.T) {
110115
),
111116
)
112117

113-
createView = plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, true, "CREATE VIEW myview AS SELECT i + 1 FROM mytable", "", "", "")
118+
createView = plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, true, "CREATE VIEW myview AS SELECT i + 1 FROM mytable", "", "", "")
114119
_, err = DefaultBuilder.buildNodeExec(ctx, createView, nil)
115120
require.NoError(t, err)
116121

@@ -124,7 +129,7 @@ func TestReplaceExistingViewNative(t *testing.T) {
124129
// the catalog when RowIter is called
125130
func TestCreateViewNative(t *testing.T) {
126131
db := memory.NewDatabase("mydb")
127-
createView := newCreateView(db, false)
132+
createView := newCreateView(db, false, false)
128133

129134
ctx := sql.NewContext(context.Background())
130135
_, err := DefaultBuilder.buildNodeExec(ctx, createView, nil)
@@ -141,7 +146,7 @@ func TestCreateViewNative(t *testing.T) {
141146
func TestCreateExistingViewWithRegistry(t *testing.T) {
142147
require := require.New(t)
143148

144-
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false)
149+
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false)
145150

146151
view := createView.View()
147152
viewReg := sql.NewViewRegistry()
@@ -161,7 +166,7 @@ func TestCreateExistingViewWithRegistry(t *testing.T) {
161166
func TestReplaceExistingViewWithRegistry(t *testing.T) {
162167
require := require.New(t)
163168

164-
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false)
169+
createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false)
165170

166171
view := sql.NewView(createView.Name, nil, "", "")
167172
viewReg := sql.NewViewRegistry()

sql/rowexec/ddl.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,19 +164,32 @@ func (b *BaseBuilder) buildCreateView(ctx *sql.Context, n *plan.CreateView, row
164164
return nil, err
165165
}
166166
for _, name := range names {
167-
if strings.ToLower(name) == strings.ToLower(n.Name) {
168-
return nil, sql.ErrTableAlreadyExists.New(n)
167+
if !strings.EqualFold(name, n.Name) {
168+
continue
169+
}
170+
if n.IfNotExists {
171+
return rowIterWithOkResultWithZeroRowsAffected(), nil
169172
}
173+
return nil, sql.ErrTableAlreadyExists.New(n)
170174
}
171175

172176
// TODO: isUpdatable should be defined at CREATE VIEW time
173177
// isUpdatable := GetIsUpdatableFromCreateView(cv)
174178
creator, ok := n.Database().(sql.ViewDatabase)
175-
if ok {
176-
return rowIterWithOkResultWithZeroRowsAffected(), creator.CreateView(ctx, n.Name, n.Definition.TextDefinition, n.CreateViewString)
177-
} else {
178-
return rowIterWithOkResultWithZeroRowsAffected(), registry.Register(n.Database().Name(), n.View())
179+
if !ok {
180+
err = registry.Register(n.Database().Name(), n.View())
181+
if err != nil {
182+
return nil, err
183+
}
184+
return rowIterWithOkResultWithZeroRowsAffected(), nil
179185
}
186+
err = creator.CreateView(ctx, n.Name, n.Definition.TextDefinition, n.CreateViewString)
187+
if err != nil {
188+
if !sql.ErrExistingView.Is(err) || !n.IfNotExists {
189+
return nil, err
190+
}
191+
}
192+
return rowIterWithOkResultWithZeroRowsAffected(), nil
180193
}
181194

182195
func (b *BaseBuilder) buildCreateCheck(ctx *sql.Context, n *plan.CreateCheck, row sql.Row) (sql.RowIter, error) {

sql/rowexec/drop_view_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func setupView(t *testing.T, db memory.MemoryDatabase) (*sql.Context, *sql.View)
4747
),
4848
)
4949

50-
createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "")
50+
createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, false, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "")
5151

5252
ctx := sql.NewContext(context.Background())
5353

0 commit comments

Comments
 (0)