diff --git a/enginetest/queries/alter_table_queries.go b/enginetest/queries/alter_table_queries.go index bec63ab273..6f22a8e893 100644 --- a/enginetest/queries/alter_table_queries.go +++ b/enginetest/queries/alter_table_queries.go @@ -1029,12 +1029,58 @@ var AlterTableScripts = []ScriptTest{ }, }, }, + { + Name: "alter table comment", + SetUpScript: []string{ + "create table t (i int)", + "create table tableWithComment (i int) comment = 'comment'", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "show create table t", + Expected: []sql.Row{{ + "t", + "CREATE TABLE `t` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin", + }}, + }, + { + Query: "show create table tableWithComment", + Expected: []sql.Row{{ + "tableWithComment", + "CREATE TABLE `tableWithComment` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='comment'", + }}, + }, + { + Query: "alter table t comment = 'new comment'", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + Query: "alter table tableWithComment comment = 'new comment'", + Expected: []sql.Row{{types.NewOkResult(0)}}, + }, + { + Query: "show create table t", + Expected: []sql.Row{{ + "t", + "CREATE TABLE `t` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='new comment'", + }}, + }, + { + Query: "show create table tableWithComment", + Expected: []sql.Row{{ + "tableWithComment", + "CREATE TABLE `tableWithComment` (\n `i` int\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='new comment'", + }}, + }, + }, + }, { Name: "alter table comments are escaped", SetUpScript: []string{ "create table t (i int);", `alter table t modify column i int comment "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`, `alter table t add column j int comment "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`, + `alter table t comment = "newline \n | return \r | backslash \\ | NUL \0 \x00 | ctrlz \Z \x1A"`, }, Assertions: []ScriptTestAssertion{ { @@ -1043,7 +1089,7 @@ var AlterTableScripts = []ScriptTest{ "t", "CREATE TABLE `t` (\n `i` int COMMENT 'newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \x1A x1A'," + "\n `j` int COMMENT 'newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \x1A x1A'\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin"}}, + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin COMMENT='newline \\n | return \\r | backslash \\\\ | NUL \\0 x00 | ctrlz \u001A x1A'"}}, }, }, }, diff --git a/go.mod b/go.mod index 9167b8e031..4009062f43 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-20250730174048-497aebb8cea7 + github.com/dolthub/vitess v0.0.0-20250813175212-45844169a751 github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d github.com/gocraft/dbr/v2 v2.7.2 github.com/google/uuid v1.3.0 diff --git a/go.sum b/go.sum index 51dd1543ff..35a9fd06ce 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81 h1:7/v8q9X github.com/dolthub/sqllogictest/go v0.0.0-20201107003712-816f3ae12d81/go.mod h1:siLfyv2c92W1eN/R4QqG/+RjjX5W2+gCTRjZxBjI3TY= github.com/dolthub/vitess v0.0.0-20250730174048-497aebb8cea7 h1:l+mWO0xoh4eG1J9gMS87opL6N6WGAQitF36R/Lg4bWs= github.com/dolthub/vitess v0.0.0-20250730174048-497aebb8cea7/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= +github.com/dolthub/vitess v0.0.0-20250813175212-45844169a751 h1:BBQKyvyODewdQxS+ICklMn1d/fFj2pVlkmMN1QFY4ms= +github.com/dolthub/vitess v0.0.0-20250813175212-45844169a751/go.mod h1:1gQZs/byeHLMSul3Lvl3MzioMtOW1je79QYGyi2fd70= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= diff --git a/memory/table.go b/memory/table.go index bde14a122f..2ba41858b5 100644 --- a/memory/table.go +++ b/memory/table.go @@ -75,6 +75,7 @@ var _ sql.TruncateableTable = (*Table)(nil) var _ sql.AlterableTable = (*Table)(nil) var _ sql.IndexAlterableTable = (*Table)(nil) var _ sql.CollationAlterableTable = (*Table)(nil) +var _ sql.CommentAlterableTable = (*Table)(nil) var _ sql.ForeignKeyTable = (*Table)(nil) var _ sql.CheckAlterableTable = (*Table)(nil) var _ sql.RewritableTable = (*Table)(nil) @@ -2188,6 +2189,13 @@ func (t *Table) ModifyDefaultCollation(ctx *sql.Context, collation sql.Collation return nil } +// ModifyComment implements sql.CommentAlterableTable +func (t *Table) ModifyComment(ctx *sql.Context, comment string) error { + data := t.sessionTableData(ctx) + data.comment = comment + return nil +} + // Filters implements the sql.FilteredTable interface. func (t *Table) Filters() []sql.Expression { return t.filters diff --git a/sql/errors.go b/sql/errors.go index 3cb3876c72..e4fdbf36e7 100644 --- a/sql/errors.go +++ b/sql/errors.go @@ -409,6 +409,9 @@ var ( // ErrAlterTableCollationNotSupported is thrown when the table doesn't support ALTER TABLE COLLATE statements ErrAlterTableCollationNotSupported = errors.NewKind("table %s cannot have its collation altered") + // ErrAlterTableCommentNotSupported is thrown when the table doesn't support ALTER TABLE COMMENT statements + ErrAlterTableCommentNotSupported = errors.NewKind("table %s cannot have its comment altered") + // ErrCollationNotSupportedOnUniqueTextIndex is thrown when a unique index is created on a TEXT column, with no // prefix length specified, and the collation is case-insensitive or accent-insensitive, meaning we can't // reliably use a content-hashed field to detect uniqueness. diff --git a/sql/plan/alter_table.go b/sql/plan/alter_table.go index 5154266487..2f45203ccd 100644 --- a/sql/plan/alter_table.go +++ b/sql/plan/alter_table.go @@ -968,3 +968,67 @@ func (atc *AlterTableCollation) WithChildren(children ...sql.Node) (sql.Node, er natc.Table = children[0] return &natc, nil } + +type AlterTableComment struct { + ddlNode + Table sql.Node + Comment string +} + +var _ sql.Node = (*AlterTableComment)(nil) +var _ sql.Databaser = (*AlterTableComment)(nil) + +func NewAlterTableComment(table *ResolvedTable, comment string) *AlterTableComment { + return &AlterTableComment{ + ddlNode: ddlNode{Db: table.SqlDatabase}, + Table: table, + Comment: comment, + } +} + +// WithDatabase implements the interface sql.Databaser +func (atc *AlterTableComment) WithDatabase(db sql.Database) (sql.Node, error) { + natc := *atc + natc.Db = db + return &natc, nil +} + +// IsReadOnly implements the interface sql.Node +func (atc *AlterTableComment) IsReadOnly() bool { + return false +} + +// String implements the interface sql.Node +func (atc *AlterTableComment) String() string { + return fmt.Sprintf("alter table %s comment %s", atc.Table.String(), atc.Comment) +} + +// DebugString implements the interface sql.Node +func (atc *AlterTableComment) DebugString() string { + return atc.String() +} + +// Resolved implements the interface sql.Node +func (atc *AlterTableComment) Resolved() bool { + return atc.Table.Resolved() && atc.ddlNode.Resolved() +} + +// Schema implements the interface sql.Node +func (atc *AlterTableComment) Schema() sql.Schema { + return atc.Table.Schema() +} + +// Children implements the interface sql.Node +func (atc *AlterTableComment) Children() []sql.Node { + return []sql.Node{atc.Table} +} + +// WithChildren implements the interface sql.Node +func (atc *AlterTableComment) WithChildren(children ...sql.Node) (sql.Node, error) { + if len(children) != 1 { + return nil, sql.ErrInvalidChildrenNumber.New(atc, len(children), 1) + } + natc := *atc + natc.Table = children[0] + return &natc, nil +} diff --git a/sql/planbuilder/ddl.go b/sql/planbuilder/ddl.go index fe2ee04140..c5f79201e9 100644 --- a/sql/planbuilder/ddl.go +++ b/sql/planbuilder/ddl.go @@ -650,6 +650,10 @@ func (b *Builder) buildAlterTableClause(inScope *scope, ddl *ast.DDL) []*scope { outScopes = append(outScopes, b.buildAlterCollationSpec(tableScope, ddl, rt)) } + if ddl.AlterCommentSpec != nil { + outScopes = append(outScopes, b.buildAlterCommentSpec(tableScope, ddl, rt)) + } + if ddl.NotNullSpec != nil { outScopes = append(outScopes, b.buildAlterNotNull(tableScope, ddl, rt)) } @@ -1176,6 +1180,12 @@ func (b *Builder) buildAlterCollationSpec(inScope *scope, ddl *ast.DDL, table *p return } +func (b *Builder) buildAlterCommentSpec(inScope *scope, ddl *ast.DDL, table *plan.ResolvedTable) (outScope *scope) { + outScope = inScope + outScope.node = plan.NewAlterTableComment(table, ddl.AlterCommentSpec.Comment) + return +} + func (b *Builder) buildDefaultExpression(inScope *scope, defaultExpr ast.Expr) *sql.ColumnDefaultValue { if defaultExpr == nil { return nil diff --git a/sql/rowexec/ddl.go b/sql/rowexec/ddl.go index 86d3abe036..3512ad79ba 100644 --- a/sql/rowexec/ddl.go +++ b/sql/rowexec/ddl.go @@ -1196,6 +1196,18 @@ func (b *BaseBuilder) buildAlterTableCollation(ctx *sql.Context, n *plan.AlterTa return rowIterWithOkResultWithZeroRowsAffected(), alterable.ModifyDefaultCollation(ctx, n.Collation) } +func (b *BaseBuilder) buildAlterTableComment(ctx *sql.Context, n *plan.AlterTableComment, row sql.Row) (sql.RowIter, error) { + tbl, err := getTableFromDatabase(ctx, n.Database(), n.Table) + if err != nil { + return nil, err + } + alterable, ok := tbl.(sql.CommentAlterableTable) + if !ok { + return nil, sql.ErrAlterTableCommentNotSupported.New(tbl.Name()) + } + return rowIterWithOkResultWithZeroRowsAffected(), alterable.ModifyComment(ctx, n.Comment) +} + func (b *BaseBuilder) buildCreateForeignKey(ctx *sql.Context, n *plan.CreateForeignKey, row sql.Row) (sql.RowIter, error) { db, err := n.DbProvider.Database(ctx, n.FkDef.Database) if err != nil { diff --git a/sql/rowexec/node_builder.gen.go b/sql/rowexec/node_builder.gen.go index f51530010d..a72dbdb0a0 100644 --- a/sql/rowexec/node_builder.gen.go +++ b/sql/rowexec/node_builder.gen.go @@ -50,6 +50,8 @@ func (b *BaseBuilder) buildNodeExecNoAnalyze(ctx *sql.Context, n sql.Node, row s return b.buildCreateForeignKey(ctx, n, row) case *plan.AlterTableCollation: return b.buildAlterTableCollation(ctx, n, row) + case *plan.AlterTableComment: + return b.buildAlterTableComment(ctx, n, row) case *plan.CreateRole: return b.buildCreateRole(ctx, n, row) case *plan.Loop: diff --git a/sql/tables.go b/sql/tables.go index c9f63bd581..ace79a60d9 100644 --- a/sql/tables.go +++ b/sql/tables.go @@ -249,6 +249,13 @@ type CollationAlterableTable interface { ModifyDefaultCollation(ctx *Context, collation CollationID) error } +// CommentAlterableTable represents a table that supports altering its comment. +type CommentAlterableTable interface { + Table + // ModifyComment modifies the table's comment + ModifyComment(ctx *Context, comment string) error +} + // PrimaryKeyTable is a table with a primary key. type PrimaryKeyTable interface { // PrimaryKeySchema returns this table's PrimaryKeySchema