Skip to content

Commit fd7fc07

Browse files
authored
implement if [not] exists logic for DDL around views and indexes (#3006)
1 parent 764333d commit fd7fc07

File tree

15 files changed

+197
-59
lines changed

15 files changed

+197
-59
lines changed

enginetest/queries/index_queries.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4338,4 +4338,63 @@ var IndexQueries = []ScriptTest{
43384338
},
43394339
},
43404340
},
4341+
{
4342+
Name: "indexes and if exists",
4343+
SetUpScript: []string{
4344+
"create table t (i int, j int);",
4345+
"create index idx on t (i);",
4346+
},
4347+
Assertions: []ScriptTestAssertion{
4348+
{
4349+
Query: "create index idx on t(j)",
4350+
ExpectedErr: sql.ErrDuplicateKey,
4351+
},
4352+
{
4353+
Query: "create index if not exists idx on t(j)",
4354+
Expected: []sql.Row{
4355+
{types.NewOkResult(0)},
4356+
},
4357+
},
4358+
{
4359+
Query: "show create table t",
4360+
Expected: []sql.Row{
4361+
{"t", "CREATE TABLE `t` (\n" +
4362+
" `i` int,\n" +
4363+
" `j` int,\n" +
4364+
" KEY `idx` (`i`)\n" +
4365+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
4366+
},
4367+
},
4368+
{
4369+
Query: "alter table t add index idx (j)",
4370+
ExpectedErr: sql.ErrDuplicateKey,
4371+
},
4372+
{
4373+
Query: "alter table t add index if not exists idx (j)",
4374+
Expected: []sql.Row{
4375+
{types.NewOkResult(0)},
4376+
},
4377+
},
4378+
{
4379+
Query: "show create table t",
4380+
Expected: []sql.Row{
4381+
{"t", "CREATE TABLE `t` (\n" +
4382+
" `i` int,\n" +
4383+
" `j` int,\n" +
4384+
" KEY `idx` (`i`)\n" +
4385+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"},
4386+
},
4387+
},
4388+
{
4389+
Query: "alter table t drop index notanidx",
4390+
ExpectedErr: sql.ErrCantDropFieldOrKey,
4391+
},
4392+
{
4393+
Query: "alter table t drop index if exists notanidx",
4394+
Expected: []sql.Row{
4395+
{types.NewOkResult(0)},
4396+
},
4397+
},
4398+
},
4399+
},
43414400
}

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 view if not exists 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 view if not exists 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{

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad
77
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71
88
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81
9-
github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0
9+
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b
1010
github.com/go-kit/kit v0.10.0
1111
github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d
1212
github.com/gocraft/dbr/v2 v2.7.2

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE
5858
github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI=
5959
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE=
6060
github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY=
61-
github.com/dolthub/vitess v0.0.0-20250530231040-bfd522856394 h1:sMwntvk7O9dttaJLqnOvy8zgk0ah9qnyWkAahfOgnIo=
62-
github.com/dolthub/vitess v0.0.0-20250530231040-bfd522856394/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
63-
github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0 h1:esaEESlRNds2thjx9mEiDRIp1Cm5nM9K2PbYqPKei5Q=
64-
github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
61+
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b h1:rgZXgRYZ3SZbb4Tz5Y6vnzvB7P9pFvEP+Q7UGfRC9uY=
62+
github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70=
6563
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
6664
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
6765
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=

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/analyzer/validate_create_table.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,9 @@ func validateAlterIndex(ctx *sql.Context, initialSch, sch sql.Schema, ai *plan.A
657657
}
658658
}
659659
if savedIdx == -1 {
660+
if ai.IfExists {
661+
return nil, nil
662+
}
660663
return nil, sql.ErrCantDropFieldOrKey.New(ai.IndexName)
661664
}
662665
// Remove the index from the list

sql/index_driver.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ var (
120120
ErrIndexExpressionAlreadyRegistered = errors.NewKind("there is already an index registered for the expressions: %s")
121121

122122
// ErrIndexNotFound is returned when the index could not be found.
123-
ErrIndexNotFound = errors.NewKind("index %q was not found")
123+
ErrIndexNotFound = errors.NewKind("index %q was not found")
124124

125125
// ErrIndexDeleteInvalidStatus is returned when the index trying to delete
126126
// does not have a ready or outdated state.

sql/plan/alter_index.go

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,13 @@ type AlterIndex struct {
4545
// Action states whether it's a CREATE, DROP, or RENAME
4646
Action IndexAction
4747
// ddlNode references to the database that is being operated on
48-
ddlNode
48+
Db sql.Database
4949
// Table is the table that is being referenced
5050
Table sql.TableNode
51+
// IfExists indicates if we should error when deleting an index that doesn't exist
52+
IfExists bool
53+
// IfNotExists indicates if we should error when creating a duplicate index
54+
IfNotExists bool
5155
// IndexName is the index name, and in the case of a RENAME it represents the new name
5256
IndexName string
5357
// PreviousIndexName states the old name when renaming an index
@@ -72,32 +76,34 @@ var _ sql.Expressioner = (*AlterIndex)(nil)
7276
var _ sql.Node = (*AlterIndex)(nil)
7377
var _ sql.CollationCoercible = (*AlterIndex)(nil)
7478

75-
func NewAlterCreateIndex(db sql.Database, table sql.TableNode, indexName string, using sql.IndexUsing, constraint sql.IndexConstraint, columns []sql.IndexColumn, comment string) *AlterIndex {
79+
func NewAlterCreateIndex(db sql.Database, table sql.TableNode, ifNotExists bool, indexName string, using sql.IndexUsing, constraint sql.IndexConstraint, columns []sql.IndexColumn, comment string) *AlterIndex {
7680
return &AlterIndex{
77-
Action: IndexAction_Create,
78-
ddlNode: ddlNode{Db: db},
79-
Table: table,
80-
IndexName: indexName,
81-
Using: using,
82-
Constraint: constraint,
83-
Columns: columns,
84-
Comment: comment,
81+
Action: IndexAction_Create,
82+
Db: db,
83+
Table: table,
84+
IfNotExists: ifNotExists,
85+
IndexName: indexName,
86+
Using: using,
87+
Constraint: constraint,
88+
Columns: columns,
89+
Comment: comment,
8590
}
8691
}
8792

88-
func NewAlterDropIndex(db sql.Database, table sql.TableNode, indexName string) *AlterIndex {
93+
func NewAlterDropIndex(db sql.Database, table sql.TableNode, ifExists bool, indexName string) *AlterIndex {
8994
return &AlterIndex{
9095
Action: IndexAction_Drop,
91-
ddlNode: ddlNode{Db: db},
96+
Db: db,
9297
Table: table,
98+
IfExists: ifExists,
9399
IndexName: indexName,
94100
}
95101
}
96102

97103
func NewAlterRenameIndex(db sql.Database, table sql.TableNode, fromIndexName, toIndexName string) *AlterIndex {
98104
return &AlterIndex{
99105
Action: IndexAction_Rename,
100-
ddlNode: ddlNode{Db: db},
106+
Db: db,
101107
Table: table,
102108
IndexName: toIndexName,
103109
PreviousIndexName: fromIndexName,
@@ -107,7 +113,7 @@ func NewAlterRenameIndex(db sql.Database, table sql.TableNode, fromIndexName, to
107113
func NewAlterDisableEnableKeys(db sql.Database, table sql.TableNode, disableKeys bool) *AlterIndex {
108114
return &AlterIndex{
109115
Action: IndexAction_DisableEnableKeys,
110-
ddlNode: ddlNode{Db: db},
116+
Db: db,
111117
Table: table,
112118
DisableKeys: disableKeys,
113119
}
@@ -120,7 +126,7 @@ func (p *AlterIndex) Schema() sql.Schema {
120126

121127
// WithChildren implements the Node interface. For AlterIndex, the only appropriate input is
122128
// a single child - The Table.
123-
func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) {
129+
func (p *AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) {
124130
if len(children) != 1 {
125131
return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1)
126132
}
@@ -131,21 +137,24 @@ func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) {
131137
}
132138
switch p.Action {
133139
case IndexAction_Create, IndexAction_Drop, IndexAction_Rename, IndexAction_DisableEnableKeys:
134-
p.Table = child
135-
return &p, nil
140+
np := *p
141+
np.Table = child
142+
return &np, nil
136143
default:
137144
return nil, ErrIndexActionNotImplemented.New(p.Action)
138145
}
139146
}
140147

141-
func (p AlterIndex) WithColumns(columns []sql.IndexColumn) (sql.Node, error) {
142-
p.Columns = columns
143-
return &p, nil
148+
func (p *AlterIndex) WithColumns(columns []sql.IndexColumn) (sql.Node, error) {
149+
np := *p
150+
np.Columns = columns
151+
return &np, nil
144152
}
145153

146-
func (p AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) {
147-
p.targetSchema = schema
148-
return &p, nil
154+
func (p *AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) {
155+
np := *p
156+
np.targetSchema = schema
157+
return &np, nil
149158
}
150159

151160
func (p *AlterIndex) TargetSchema() sql.Schema {
@@ -164,7 +173,7 @@ func (p *AlterIndex) Expressions() []sql.Expression {
164173

165174
// WithExpressions implements the Node Interface. For AlterIndex, expressions represent column defaults on the
166175
// targetSchema instance - required to be the same number of columns on the target schema.
167-
func (p AlterIndex) WithExpressions(expressions ...sql.Expression) (sql.Node, error) {
176+
func (p *AlterIndex) WithExpressions(expressions ...sql.Expression) (sql.Node, error) {
168177
columns := p.TargetSchema().Copy()
169178

170179
if len(columns) != len(expressions) {
@@ -209,7 +218,7 @@ func (p *AlterIndex) WithDatabase(database sql.Database) (sql.Node, error) {
209218
return &np, nil
210219
}
211220

212-
func (p AlterIndex) String() string {
221+
func (p *AlterIndex) String() string {
213222
pr := sql.NewTreePrinter()
214223
switch p.Action {
215224
case IndexAction_Create:
@@ -259,7 +268,7 @@ func (p AlterIndex) String() string {
259268
}
260269

261270
func (p *AlterIndex) Resolved() bool {
262-
return p.Table.Resolved() && p.ddlNode.Resolved() && p.targetSchema.Resolved()
271+
return p.Table.Resolved() && p.targetSchema.Resolved()
263272
}
264273

265274
func (p *AlterIndex) IsReadOnly() bool {

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

0 commit comments

Comments
 (0)