diff --git a/enginetest/queries/index_queries.go b/enginetest/queries/index_queries.go index a09de816ac..fdb72b9be7 100644 --- a/enginetest/queries/index_queries.go +++ b/enginetest/queries/index_queries.go @@ -4338,4 +4338,63 @@ var IndexQueries = []ScriptTest{ }, }, }, + { + Name: "indexes and if exists", + SetUpScript: []string{ + "create table t (i int, j int);", + "create index idx on t (i);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "create index idx on t(j)", + ExpectedErr: sql.ErrDuplicateKey, + }, + { + Query: "create index if not exists idx on t(j)", + Expected: []sql.Row{ + {types.NewOkResult(0)}, + }, + }, + { + Query: "show create table t", + Expected: []sql.Row{ + {"t", "CREATE TABLE `t` (\n" + + " `i` int,\n" + + " `j` int,\n" + + " KEY `idx` (`i`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}, + }, + }, + { + Query: "alter table t add index idx (j)", + ExpectedErr: sql.ErrDuplicateKey, + }, + { + Query: "alter table t add index if not exists idx (j)", + Expected: []sql.Row{ + {types.NewOkResult(0)}, + }, + }, + { + Query: "show create table t", + Expected: []sql.Row{ + {"t", "CREATE TABLE `t` (\n" + + " `i` int,\n" + + " `j` int,\n" + + " KEY `idx` (`i`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}, + }, + }, + { + Query: "alter table t drop index notanidx", + ExpectedErr: sql.ErrCantDropFieldOrKey, + }, + { + Query: "alter table t drop index if exists notanidx", + Expected: []sql.Row{ + {types.NewOkResult(0)}, + }, + }, + }, + }, } diff --git a/enginetest/queries/view_queries.go b/enginetest/queries/view_queries.go index dce470f12b..55d6f8ddaa 100644 --- a/enginetest/queries/view_queries.go +++ b/enginetest/queries/view_queries.go @@ -20,6 +20,36 @@ import ( ) var ViewScripts = []ScriptTest{ + { + Name: "existing views", + SetUpScript: []string{ + "create view v as select 1;", + "create table t (i int);", + "insert into t values (1);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "create view if not exists v as select 2;", + Expected: []sql.Row{ + {types.NewOkResult(0)}, + }, + }, + { + Query: "select * from v;", + Expected: []sql.Row{{1}}, + }, + { + Query: "create view if not exists t as select 2;", + Expected: []sql.Row{ + {types.NewOkResult(0)}, + }, + }, + { + Query: "select * from t;", + Expected: []sql.Row{{1}}, + }, + }, + }, { Name: "multi database view", SetUpScript: []string{ diff --git a/go.mod b/go.mod index d843536c66..81211175d8 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/dolthub/go-icu-regex v0.0.0-20250327004329-6799764f2dad github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 - github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0 + github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b github.com/go-kit/kit v0.10.0 github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d github.com/gocraft/dbr/v2 v2.7.2 diff --git a/go.sum b/go.sum index a17c249862..0879605e74 100644 --- a/go.sum +++ b/go.sum @@ -58,10 +58,8 @@ github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71 h1:bMGS25NWAGTE github.com/dolthub/jsonpath v0.0.2-0.20240227200619-19675ab05c71/go.mod h1:2/2zjLQ/JOOSbbSboojeg+cAwcRV0fDLzIiWch/lhqI= github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9XGFa6q5Ap4Z/OhNkAMBaK5YeuEzwJt+NZdhiE= github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY= -github.com/dolthub/vitess v0.0.0-20250530231040-bfd522856394 h1:sMwntvk7O9dttaJLqnOvy8zgk0ah9qnyWkAahfOgnIo= -github.com/dolthub/vitess v0.0.0-20250530231040-bfd522856394/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= -github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0 h1:esaEESlRNds2thjx9mEiDRIp1Cm5nM9K2PbYqPKei5Q= -github.com/dolthub/vitess v0.0.0-20250604183723-cad71bbe21d0/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= +github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b h1:rgZXgRYZ3SZbb4Tz5Y6vnzvB7P9pFvEP+Q7UGfRC9uY= +github.com/dolthub/vitess v0.0.0-20250605180032-fa2a634c215b/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= diff --git a/memory/database.go b/memory/database.go index 41b7fa4fe4..38ed8d4a96 100644 --- a/memory/database.go +++ b/memory/database.go @@ -555,7 +555,7 @@ func (d *Database) Database() *BaseDatabase { func (d *Database) CreateView(ctx *sql.Context, name string, selectStatement, createViewStmt string) error { _, ok := d.views[strings.ToLower(name)] if ok { - return sql.ErrExistingView.New(name) + return sql.ErrExistingView.New(d.Name(), name) } sqlMode := sql.LoadSqlMode(ctx) diff --git a/sql/analyzer/validate_create_table.go b/sql/analyzer/validate_create_table.go index 39f9124f1e..edda379530 100644 --- a/sql/analyzer/validate_create_table.go +++ b/sql/analyzer/validate_create_table.go @@ -657,6 +657,9 @@ func validateAlterIndex(ctx *sql.Context, initialSch, sch sql.Schema, ai *plan.A } } if savedIdx == -1 { + if ai.IfExists { + return nil, nil + } return nil, sql.ErrCantDropFieldOrKey.New(ai.IndexName) } // Remove the index from the list diff --git a/sql/index_driver.go b/sql/index_driver.go index a70eba78f5..52f975fbd7 100644 --- a/sql/index_driver.go +++ b/sql/index_driver.go @@ -120,7 +120,7 @@ var ( ErrIndexExpressionAlreadyRegistered = errors.NewKind("there is already an index registered for the expressions: %s") // ErrIndexNotFound is returned when the index could not be found. - ErrIndexNotFound = errors.NewKind("index %q was not found") + ErrIndexNotFound = errors.NewKind("index %q was not found") // ErrIndexDeleteInvalidStatus is returned when the index trying to delete // does not have a ready or outdated state. diff --git a/sql/plan/alter_index.go b/sql/plan/alter_index.go index 4e830be4fb..1ca80a72a1 100644 --- a/sql/plan/alter_index.go +++ b/sql/plan/alter_index.go @@ -45,9 +45,13 @@ type AlterIndex struct { // Action states whether it's a CREATE, DROP, or RENAME Action IndexAction // ddlNode references to the database that is being operated on - ddlNode + Db sql.Database // Table is the table that is being referenced Table sql.TableNode + // IfExists indicates if we should error when deleting an index that doesn't exist + IfExists bool + // IfNotExists indicates if we should error when creating a duplicate index + IfNotExists bool // IndexName is the index name, and in the case of a RENAME it represents the new name IndexName string // PreviousIndexName states the old name when renaming an index @@ -72,24 +76,26 @@ var _ sql.Expressioner = (*AlterIndex)(nil) var _ sql.Node = (*AlterIndex)(nil) var _ sql.CollationCoercible = (*AlterIndex)(nil) -func NewAlterCreateIndex(db sql.Database, table sql.TableNode, indexName string, using sql.IndexUsing, constraint sql.IndexConstraint, columns []sql.IndexColumn, comment string) *AlterIndex { +func NewAlterCreateIndex(db sql.Database, table sql.TableNode, ifNotExists bool, indexName string, using sql.IndexUsing, constraint sql.IndexConstraint, columns []sql.IndexColumn, comment string) *AlterIndex { return &AlterIndex{ - Action: IndexAction_Create, - ddlNode: ddlNode{Db: db}, - Table: table, - IndexName: indexName, - Using: using, - Constraint: constraint, - Columns: columns, - Comment: comment, + Action: IndexAction_Create, + Db: db, + Table: table, + IfNotExists: ifNotExists, + IndexName: indexName, + Using: using, + Constraint: constraint, + Columns: columns, + Comment: comment, } } -func NewAlterDropIndex(db sql.Database, table sql.TableNode, indexName string) *AlterIndex { +func NewAlterDropIndex(db sql.Database, table sql.TableNode, ifExists bool, indexName string) *AlterIndex { return &AlterIndex{ Action: IndexAction_Drop, - ddlNode: ddlNode{Db: db}, + Db: db, Table: table, + IfExists: ifExists, IndexName: indexName, } } @@ -97,7 +103,7 @@ func NewAlterDropIndex(db sql.Database, table sql.TableNode, indexName string) * func NewAlterRenameIndex(db sql.Database, table sql.TableNode, fromIndexName, toIndexName string) *AlterIndex { return &AlterIndex{ Action: IndexAction_Rename, - ddlNode: ddlNode{Db: db}, + Db: db, Table: table, IndexName: toIndexName, PreviousIndexName: fromIndexName, @@ -107,7 +113,7 @@ func NewAlterRenameIndex(db sql.Database, table sql.TableNode, fromIndexName, to func NewAlterDisableEnableKeys(db sql.Database, table sql.TableNode, disableKeys bool) *AlterIndex { return &AlterIndex{ Action: IndexAction_DisableEnableKeys, - ddlNode: ddlNode{Db: db}, + Db: db, Table: table, DisableKeys: disableKeys, } @@ -120,7 +126,7 @@ func (p *AlterIndex) Schema() sql.Schema { // WithChildren implements the Node interface. For AlterIndex, the only appropriate input is // a single child - The Table. -func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) { +func (p *AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) { if len(children) != 1 { return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1) } @@ -131,21 +137,24 @@ func (p AlterIndex) WithChildren(children ...sql.Node) (sql.Node, error) { } switch p.Action { case IndexAction_Create, IndexAction_Drop, IndexAction_Rename, IndexAction_DisableEnableKeys: - p.Table = child - return &p, nil + np := *p + np.Table = child + return &np, nil default: return nil, ErrIndexActionNotImplemented.New(p.Action) } } -func (p AlterIndex) WithColumns(columns []sql.IndexColumn) (sql.Node, error) { - p.Columns = columns - return &p, nil +func (p *AlterIndex) WithColumns(columns []sql.IndexColumn) (sql.Node, error) { + np := *p + np.Columns = columns + return &np, nil } -func (p AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) { - p.targetSchema = schema - return &p, nil +func (p *AlterIndex) WithTargetSchema(schema sql.Schema) (sql.Node, error) { + np := *p + np.targetSchema = schema + return &np, nil } func (p *AlterIndex) TargetSchema() sql.Schema { @@ -164,7 +173,7 @@ func (p *AlterIndex) Expressions() []sql.Expression { // WithExpressions implements the Node Interface. For AlterIndex, expressions represent column defaults on the // targetSchema instance - required to be the same number of columns on the target schema. -func (p AlterIndex) WithExpressions(expressions ...sql.Expression) (sql.Node, error) { +func (p *AlterIndex) WithExpressions(expressions ...sql.Expression) (sql.Node, error) { columns := p.TargetSchema().Copy() if len(columns) != len(expressions) { @@ -209,7 +218,7 @@ func (p *AlterIndex) WithDatabase(database sql.Database) (sql.Node, error) { return &np, nil } -func (p AlterIndex) String() string { +func (p *AlterIndex) String() string { pr := sql.NewTreePrinter() switch p.Action { case IndexAction_Create: @@ -259,7 +268,7 @@ func (p AlterIndex) String() string { } func (p *AlterIndex) Resolved() bool { - return p.Table.Resolved() && p.ddlNode.Resolved() && p.targetSchema.Resolved() + return p.Table.Resolved() && p.targetSchema.Resolved() } func (p *AlterIndex) IsReadOnly() bool { diff --git a/sql/plan/create_view.go b/sql/plan/create_view.go index 1c6f8e5541..bbfdcae198 100644 --- a/sql/plan/create_view.go +++ b/sql/plan/create_view.go @@ -31,6 +31,7 @@ type CreateView struct { database sql.Database targetSchema sql.Schema Name string + IfNotExists bool IsReplace bool Definition *SubqueryAlias CreateViewString string @@ -46,11 +47,12 @@ var _ sql.SchemaTarget = (*CreateView)(nil) // NewCreateView creates a CreateView node with the specified parameters, // setting its catalog to nil. -func NewCreateView(database sql.Database, name string, definition *SubqueryAlias, isReplace bool, createViewStr, algorithm, definer, security string) *CreateView { +func NewCreateView(database sql.Database, name string, definition *SubqueryAlias, ifNotExists, isReplace bool, createViewStr, algorithm, definer, security string) *CreateView { return &CreateView{ UnaryNode: UnaryNode{Child: definition}, database: database, Name: name, + IfNotExists: ifNotExists, IsReplace: isReplace, Definition: definition, CreateViewString: createViewStr, diff --git a/sql/planbuilder/create_ddl.go b/sql/planbuilder/create_ddl.go index dab4aacfc6..dfa70743da 100644 --- a/sql/planbuilder/create_ddl.go +++ b/sql/planbuilder/create_ddl.go @@ -672,7 +672,7 @@ func (b *Builder) buildCreateView(inScope *scope, subQuery string, fullQuery str if !ok { b.handleErr(sql.ErrDatabaseSchemaNotFound.New(c.Table.SchemaQualifier.String())) } - createView := plan.NewCreateView(db, c.ViewSpec.ViewName.Name.String(), queryAlias, c.OrReplace, subQuery, c.ViewSpec.Algorithm, definer, c.ViewSpec.Security) + createView := plan.NewCreateView(db, c.ViewSpec.ViewName.Name.String(), queryAlias, c.IfNotExists, c.OrReplace, subQuery, c.ViewSpec.Algorithm, definer, c.ViewSpec.Security) outScope.node = b.modifySchemaTarget(queryScope, createView, createView.Definition.Schema()) return outScope diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index ca09797bcb..3d3b65e0e3 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -612,6 +612,7 @@ func (b *Builder) buildAlterTableClause(inScope *scope, ddl *ast.DDL) []*scope { createIndex := plan.NewAlterCreateIndex( rt.Database(), rt, + ddl.IfNotExists, column.Name.String(), sql.IndexUsing_BTree, sql.IndexConstraint_Unique, @@ -994,7 +995,16 @@ func (b *Builder) buildAlterIndex(inScope *scope, ddl *ast.DDL, table *plan.Reso b.handleErr(err) } - createIndex := plan.NewAlterCreateIndex(table.SqlDatabase, table, ddl.IndexSpec.ToName.String(), using, constraint, columns, comment) + createIndex := plan.NewAlterCreateIndex( + table.SqlDatabase, + table, + ddl.IfNotExists, + ddl.IndexSpec.ToName.String(), + using, + constraint, + columns, + comment, + ) outScope.node = b.modifySchemaTarget(inScope, createIndex, table.Schema()) return case ast.DropStr: @@ -1002,7 +1012,7 @@ func (b *Builder) buildAlterIndex(inScope *scope, ddl *ast.DDL, table *plan.Reso outScope.node = plan.NewAlterDropPk(table.SqlDatabase, table) return } - outScope.node = plan.NewAlterDropIndex(table.Database(), table, ddl.IndexSpec.ToName.String()) + outScope.node = plan.NewAlterDropIndex(table.Database(), table, ddl.IfExists, ddl.IndexSpec.ToName.String()) return case ast.RenameStr: outScope.node = plan.NewAlterRenameIndex(table.Database(), table, ddl.IndexSpec.FromName.String(), ddl.IndexSpec.ToName.String()) diff --git a/sql/rowexec/create_view_test.go b/sql/rowexec/create_view_test.go index c04777cd8b..c65d3afcab 100644 --- a/sql/rowexec/create_view_test.go +++ b/sql/rowexec/create_view_test.go @@ -27,7 +27,7 @@ import ( "github.com/dolthub/go-mysql-server/sql/types" ) -func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView { +func newCreateView(db memory.MemoryDatabase, ifNotExists, isReplace bool) *plan.CreateView { table := memory.NewTable(db.Database(), "mytable", sql.NewPrimaryKeySchema(sql.Schema{ {Name: "i", Source: "mytable", Type: types.Int32}, {Name: "s", Source: "mytable", Type: types.Text}, @@ -44,7 +44,7 @@ func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView { ), ) - createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, isReplace, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "") + createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, ifNotExists, isReplace, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "") return createView } @@ -54,7 +54,7 @@ func newCreateView(db memory.MemoryDatabase, isReplace bool) *plan.CreateView { func TestCreateViewWithRegistry(t *testing.T) { require := require.New(t) - createView := newCreateView(memory.NewViewlessDatabase("mydb"), false) + createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false) ctx := sql.NewContext(context.Background()) _, err := DefaultBuilder.buildNodeExec(ctx, createView, nil) @@ -68,7 +68,8 @@ func TestCreateViewWithRegistry(t *testing.T) { // Tests that CreateView RowIter returns an error when the view exists func TestCreateExistingViewNative(t *testing.T) { - createView := newCreateView(memory.NewDatabase("mydb"), false) + createView := newCreateView(memory.NewDatabase("mydb"), false, false) + createExistingView := newCreateView(memory.NewDatabase("mydb"), true, false) ctx := sql.NewContext(context.Background()) _, err := DefaultBuilder.buildNodeExec(ctx, createView, nil) @@ -78,13 +79,17 @@ func TestCreateExistingViewNative(t *testing.T) { _, err = DefaultBuilder.buildNodeExec(ctx, createView, nil) require.Error(t, err) require.True(t, sql.ErrExistingView.Is(err)) + + ctx = sql.NewContext(context.Background()) + _, err = DefaultBuilder.buildNodeExec(ctx, createExistingView, nil) + require.NoError(t, err) } // Tests that CreateView RowIter succeeds when the view exists and the // IsReplace flag is set to true func TestReplaceExistingViewNative(t *testing.T) { db := memory.NewDatabase("mydb") - createView := newCreateView(db, false) + createView := newCreateView(db, false, false) ctx := sql.NewContext(context.Background()) _, err := DefaultBuilder.buildNodeExec(ctx, createView, nil) @@ -110,7 +115,7 @@ func TestReplaceExistingViewNative(t *testing.T) { ), ) - createView = plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, true, "CREATE VIEW myview AS SELECT i + 1 FROM mytable", "", "", "") + createView = plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, true, "CREATE VIEW myview AS SELECT i + 1 FROM mytable", "", "", "") _, err = DefaultBuilder.buildNodeExec(ctx, createView, nil) require.NoError(t, err) @@ -124,7 +129,7 @@ func TestReplaceExistingViewNative(t *testing.T) { // the catalog when RowIter is called func TestCreateViewNative(t *testing.T) { db := memory.NewDatabase("mydb") - createView := newCreateView(db, false) + createView := newCreateView(db, false, false) ctx := sql.NewContext(context.Background()) _, err := DefaultBuilder.buildNodeExec(ctx, createView, nil) @@ -141,7 +146,7 @@ func TestCreateViewNative(t *testing.T) { func TestCreateExistingViewWithRegistry(t *testing.T) { require := require.New(t) - createView := newCreateView(memory.NewViewlessDatabase("mydb"), false) + createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false) view := createView.View() viewReg := sql.NewViewRegistry() @@ -161,7 +166,7 @@ func TestCreateExistingViewWithRegistry(t *testing.T) { func TestReplaceExistingViewWithRegistry(t *testing.T) { require := require.New(t) - createView := newCreateView(memory.NewViewlessDatabase("mydb"), false) + createView := newCreateView(memory.NewViewlessDatabase("mydb"), false, false) view := sql.NewView(createView.Name, nil, "", "") viewReg := sql.NewViewRegistry() diff --git a/sql/rowexec/ddl.go b/sql/rowexec/ddl.go index 21335e318c..86d3abe036 100644 --- a/sql/rowexec/ddl.go +++ b/sql/rowexec/ddl.go @@ -164,19 +164,32 @@ func (b *BaseBuilder) buildCreateView(ctx *sql.Context, n *plan.CreateView, row return nil, err } for _, name := range names { - if strings.ToLower(name) == strings.ToLower(n.Name) { - return nil, sql.ErrTableAlreadyExists.New(n) + if !strings.EqualFold(name, n.Name) { + continue + } + if n.IfNotExists { + return rowIterWithOkResultWithZeroRowsAffected(), nil } + return nil, sql.ErrTableAlreadyExists.New(n) } // TODO: isUpdatable should be defined at CREATE VIEW time // isUpdatable := GetIsUpdatableFromCreateView(cv) creator, ok := n.Database().(sql.ViewDatabase) - if ok { - return rowIterWithOkResultWithZeroRowsAffected(), creator.CreateView(ctx, n.Name, n.Definition.TextDefinition, n.CreateViewString) - } else { - return rowIterWithOkResultWithZeroRowsAffected(), registry.Register(n.Database().Name(), n.View()) + if !ok { + err = registry.Register(n.Database().Name(), n.View()) + if err != nil { + return nil, err + } + return rowIterWithOkResultWithZeroRowsAffected(), nil } + err = creator.CreateView(ctx, n.Name, n.Definition.TextDefinition, n.CreateViewString) + if err != nil { + if !sql.ErrExistingView.Is(err) || !n.IfNotExists { + return nil, err + } + } + return rowIterWithOkResultWithZeroRowsAffected(), nil } func (b *BaseBuilder) buildCreateCheck(ctx *sql.Context, n *plan.CreateCheck, row sql.Row) (sql.RowIter, error) { diff --git a/sql/rowexec/ddl_iters.go b/sql/rowexec/ddl_iters.go index dc29e62cea..84da759376 100644 --- a/sql/rowexec/ddl_iters.go +++ b/sql/rowexec/ddl_iters.go @@ -1950,8 +1950,7 @@ func getFulltextDatabase(db sql.Database) (fulltext.Database, error) { // Execute inserts the rows in the database. func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) error { // We should refresh the state of the table in case this alter was in a multi alter statement. - db := n.Database() - table, err := getTableFromDatabase(ctx, db, n.Table) + table, err := getTableFromDatabase(ctx, n.Db, n.Table) if err != nil { return err } @@ -1986,7 +1985,7 @@ func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) er if indexDef.IsFullText() { var database fulltext.Database - database, err = getFulltextDatabase(db) + database, err = getFulltextDatabase(n.Db) if err != nil { return err } @@ -1999,6 +1998,9 @@ func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) er err = idxAltTbl.CreateIndex(ctx, indexDef) if err != nil { + if sql.ErrDuplicateKey.Is(err) && n.IfNotExists { + return nil + } return err } @@ -2062,7 +2064,7 @@ func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) er // If we're dropping a Full-Text, then we also need to delete its tables if addressable, ok := idxAltTbl.(sql.IndexAddressableTable); !ok { // If they don't support their creation, then it's safe to assume that they won't have any to delete - if _, err = getFulltextDatabase(db); err != nil { + if _, err = getFulltextDatabase(n.Db); err != nil { return err } } else { @@ -2091,7 +2093,7 @@ func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) er } // We found the index and it is Full-Text, so we need to delete the other tables if ftIndex != nil { - dropper, ok := db.(sql.TableDropper) + dropper, ok := n.Db.(sql.TableDropper) if !ok { return sql.ErrIncompleteFullTextIntegration.New() } @@ -2119,7 +2121,14 @@ func (b *BaseBuilder) executeAlterIndex(ctx *sql.Context, n *plan.AlterIndex) er } } } - return idxAltTbl.DropIndex(ctx, n.IndexName) + err = idxAltTbl.DropIndex(ctx, n.IndexName) + if err != nil { + if sql.ErrIndexNotFound.Is(err) && n.IfExists { + return nil + } + return err + } + return nil case plan.IndexAction_Rename: return idxAltTbl.RenameIndex(ctx, n.PreviousIndexName, n.IndexName) case plan.IndexAction_DisableEnableKeys: diff --git a/sql/rowexec/drop_view_test.go b/sql/rowexec/drop_view_test.go index 8d2a3cdf6a..12c0308b4a 100644 --- a/sql/rowexec/drop_view_test.go +++ b/sql/rowexec/drop_view_test.go @@ -47,7 +47,7 @@ func setupView(t *testing.T, db memory.MemoryDatabase) (*sql.Context, *sql.View) ), ) - createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "") + createView := plan.NewCreateView(db, subqueryAlias.Name(), subqueryAlias, false, false, "CREATE VIEW myview AS SELECT i FROM mytable", "", "", "") ctx := sql.NewContext(context.Background())