Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrator support to change table column #813

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/database/schema/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package schema
type ColumnDefinition interface {
// AutoIncrement set the column as auto increment
AutoIncrement() ColumnDefinition
// Change the column
Change() ColumnDefinition
// Comment sets the comment value
Comment(comment string) ColumnDefinition
// Default set the default value
Expand Down Expand Up @@ -37,6 +39,8 @@ type ColumnDefinition interface {
GetUseCurrent() bool
// GetUseCurrentOnUpdate returns the useCurrentOnUpdate value
GetUseCurrentOnUpdate() bool
// IsChange returns true if the column has changed
IsChange() bool
// IsSetComment returns true if the comment value is set
IsSetComment() bool
// OnUpdate sets the column to use the value on update (Mysql only)
Expand Down
2 changes: 2 additions & 0 deletions contracts/database/schema/grammar.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package schema
type Grammar interface {
// CompileAdd Compile an add column command.
CompileAdd(blueprint Blueprint, command *Command) string
// CompileChange Compile a change column command.
CompileChange(blueprint Blueprint, command *Command) []string
// CompileColumns Compile the query to determine the columns.
CompileColumns(schema, table string) string
// CompileComment Compile a column comment command.
Expand Down
6 changes: 6 additions & 0 deletions database/schema/blueprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ func (r *Blueprint) ToSql(grammar schema.Grammar) []string {

switch command.Name {
case constants.CommandAdd:
if command.Column.IsChange() {
if statement := grammar.CompileChange(r, command); statement != nil {
almas1992 marked this conversation as resolved.
Show resolved Hide resolved
statements = append(statements, statement...)
}
continue
}
statements = append(statements, grammar.CompileAdd(r, command))
case constants.CommandComment:
if statement := grammar.CompileComment(r, command); statement != "" {
Expand Down
21 changes: 21 additions & 0 deletions database/schema/blueprint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,15 @@ func (s *BlueprintTestSuite) TestToSql() {
} else {
s.Empty(s.blueprint.ToSql(grammar))
}

// Change column
s.SetupTest()
s.blueprint.String("name", 100).Change()
if driver == database.DriverPostgres {
s.Len(s.blueprint.ToSql(grammar), 1)
} else {
s.Empty(s.blueprint.ToSql(grammar))
}
}
}

Expand Down Expand Up @@ -438,3 +447,15 @@ func (s *BlueprintTestSuite) TestUnsignedTinyInteger() {
unsigned: convert.Pointer(true),
})
}

func (s *BlueprintTestSuite) TestChange() {
column := "name"
customLength := 100
s.blueprint.String(column, customLength).Change()
s.Contains(s.blueprint.GetAddedColumns(), &ColumnDefinition{
length: &customLength,
name: &column,
change: true,
ttype: convert.Pointer("string"),
})
}
11 changes: 11 additions & 0 deletions database/schema/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
type ColumnDefinition struct {
allowed []any
autoIncrement *bool
change bool
comment *string
def any
length *int
Expand All @@ -29,6 +30,12 @@ func (r *ColumnDefinition) AutoIncrement() schema.ColumnDefinition {
return r
}

func (r *ColumnDefinition) Change() schema.ColumnDefinition {
r.change = true

return r
}

func (r *ColumnDefinition) Comment(comment string) schema.ColumnDefinition {
r.comment = &comment

Expand Down Expand Up @@ -149,6 +156,10 @@ func (r *ColumnDefinition) GetUseCurrentOnUpdate() bool {
return false
}

func (r *ColumnDefinition) IsChange() bool {
return r.change
}

func (r *ColumnDefinition) IsSetComment() bool {
return r != nil && r.comment != nil
}
Expand Down
7 changes: 7 additions & 0 deletions database/schema/column_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ func (s *ColumnDefinitionTestSuite) GetType() {
s.Equal("string", s.columnDefinition.GetType())
}

func (s *ColumnDefinitionTestSuite) IsChange() {
s.False(s.columnDefinition.IsChange())

s.columnDefinition.Change()
s.True(s.columnDefinition.IsChange())
}

func (s *ColumnDefinitionTestSuite) Unsigned() {
s.columnDefinition.Unsigned()
s.True(*s.columnDefinition.unsigned)
Expand Down
6 changes: 6 additions & 0 deletions database/schema/grammars/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func (r *Mysql) CompileAdd(blueprint schema.Blueprint, command *schema.Command)
return fmt.Sprintf("alter table %s add %s", r.wrap.Table(blueprint.GetTableName()), r.getColumn(blueprint, command.Column))
}

func (r *Mysql) CompileChange(blueprint schema.Blueprint, command *schema.Command) []string {
return []string{
fmt.Sprintf("alter table %s modify %s", r.wrap.Table(blueprint.GetTableName()), r.getColumn(blueprint, command.Column)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently do not have a rename column feature, so we cannot fully replicate Laravel’s SQL at the moment.

}
}

func (r *Mysql) CompileColumns(schema, table string) string {
return fmt.Sprintf(
"select column_name as `name`, data_type as `type_name`, column_type as `type`, "+
Expand Down
21 changes: 21 additions & 0 deletions database/schema/grammars/mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ func (s *MysqlSuite) TestCompileAdd() {
s.Equal("alter table `goravel_users` add `name` varchar(1) not null default 'goravel' comment 'comment'", sql)
}

func (s *MysqlSuite) TestCompileChange() {
mockBlueprint := mocksschema.NewBlueprint(s.T())
mockColumn := mocksschema.NewColumnDefinition(s.T())

mockBlueprint.EXPECT().GetTableName().Return("users").Once()
mockColumn.EXPECT().GetName().Return("name").Once()
mockColumn.EXPECT().GetType().Return("string").Twice()
mockColumn.EXPECT().GetDefault().Return("goravel").Twice()
mockColumn.EXPECT().GetNullable().Return(false).Once()
mockColumn.EXPECT().GetLength().Return(1).Once()
mockColumn.EXPECT().GetOnUpdate().Return(nil).Once()
mockColumn.EXPECT().GetComment().Return("comment").Once()
mockColumn.EXPECT().GetUnsigned().Return(false).Once()

sql := s.grammar.CompileChange(mockBlueprint, &contractsschema.Command{
Column: mockColumn,
})

s.Equal([]string{"alter table `goravel_users` modify `name` varchar(1) not null default 'goravel' comment 'comment'"}, sql)
}

func (s *MysqlSuite) TestCompileCreate() {
mockColumn1 := mocksschema.NewColumnDefinition(s.T())
mockColumn2 := mocksschema.NewColumnDefinition(s.T())
Expand Down
73 changes: 50 additions & 23 deletions database/schema/grammars/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,29 @@
return fmt.Sprintf("alter table %s add column %s", r.wrap.Table(blueprint.GetTableName()), r.getColumn(blueprint, command.Column))
}

func (r *Postgres) CompileChange(blueprint schema.Blueprint, command *schema.Command) []string {
changes := []string{fmt.Sprintf("alter column %s type %s", r.wrap.Column(command.Column.GetName()), getType(r, command.Column))}
for _, modifier := range r.modifiers {
if change := modifier(blueprint, command.Column); change != "" {
changes = append(changes, fmt.Sprintf("alter column %s%s", r.wrap.Column(command.Column.GetName()), change))
}
}

return []string{
fmt.Sprintf("alter table %s %s", r.wrap.Table(blueprint.GetTableName()), strings.Join(changes, ", ")),
}
}

func (r *Postgres) CompileColumns(schema, table string) string {
return fmt.Sprintf(
"select a.attname as name, t.typname as type_name, format_type(a.atttypid, a.atttypmod) as type, "+
"(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, "+
"not a.attnotnull as nullable, "+
"(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, "+
"col_description(c.oid, a.attnum) as comment "+
"from pg_attribute a, pg_class c, pg_type t, pg_namespace n "+
"where c.relname = %s and n.nspname = %s and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid and n.oid = c.relnamespace "+
"order by a.attnum", r.wrap.Quote(table), r.wrap.Quote(schema))
"(select tc.collcollate from pg_catalog.pg_collation tc where tc.oid = a.attcollation) as collation, "+
"not a.attnotnull as nullable, "+
"(select pg_get_expr(adbin, adrelid) from pg_attrdef where c.oid = pg_attrdef.adrelid and pg_attrdef.adnum = a.attnum) as default, "+
"col_description(c.oid, a.attnum) as comment "+
"from pg_attribute a, pg_class c, pg_type t, pg_namespace n "+
"where c.relname = %s and n.nspname = %s and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid and n.oid = c.relnamespace "+
"order by a.attnum", r.wrap.Quote(table), r.wrap.Quote(schema))

Check warning on line 64 in database/schema/grammars/postgres.go

View check run for this annotation

Codecov / codecov/patch

database/schema/grammars/postgres.go#L58-L64

Added lines #L58 - L64 were not covered by tests
}

func (r *Postgres) CompileComment(blueprint schema.Blueprint, command *schema.Command) string {
Expand Down Expand Up @@ -194,16 +207,16 @@
func (r *Postgres) CompileIndexes(schema, table string) string {
return fmt.Sprintf(
"select ic.relname as name, string_agg(a.attname, ',' order by indseq.ord) as columns, "+
"am.amname as \"type\", i.indisunique as \"unique\", i.indisprimary as \"primary\" "+
"from pg_index i "+
"join pg_class tc on tc.oid = i.indrelid "+
"join pg_namespace tn on tn.oid = tc.relnamespace "+
"join pg_class ic on ic.oid = i.indexrelid "+
"join pg_am am on am.oid = ic.relam "+
"join lateral unnest(i.indkey) with ordinality as indseq(num, ord) on true "+
"left join pg_attribute a on a.attrelid = i.indrelid and a.attnum = indseq.num "+
"where tc.relname = %s and tn.nspname = %s "+
"group by ic.relname, am.amname, i.indisunique, i.indisprimary",
"am.amname as \"type\", i.indisunique as \"unique\", i.indisprimary as \"primary\" "+
"from pg_index i "+
"join pg_class tc on tc.oid = i.indrelid "+
"join pg_namespace tn on tn.oid = tc.relnamespace "+
"join pg_class ic on ic.oid = i.indexrelid "+
"join pg_am am on am.oid = ic.relam "+
"join lateral unnest(i.indkey) with ordinality as indseq(num, ord) on true "+
"left join pg_attribute a on a.attrelid = i.indrelid and a.attnum = indseq.num "+
"where tc.relname = %s and tn.nspname = %s "+
"group by ic.relname, am.amname, i.indisunique, i.indisprimary",

Check warning on line 219 in database/schema/grammars/postgres.go

View check run for this annotation

Codecov / codecov/patch

database/schema/grammars/postgres.go#L210-L219

Added lines #L210 - L219 were not covered by tests
r.wrap.Quote(table),
r.wrap.Quote(schema),
)
Expand All @@ -225,9 +238,9 @@

func (r *Postgres) CompileTables(_ string) string {
return "select c.relname as name, n.nspname as schema, pg_total_relation_size(c.oid) as size, " +
"obj_description(c.oid, 'pg_class') as comment from pg_class c, pg_namespace n " +
"where c.relkind in ('r', 'p') and n.oid = c.relnamespace and n.nspname not in ('pg_catalog', 'information_schema') " +
"order by c.relname"
"obj_description(c.oid, 'pg_class') as comment from pg_class c, pg_namespace n " +
"where c.relkind in ('r', 'p') and n.oid = c.relnamespace and n.nspname not in ('pg_catalog', 'information_schema') " +
"order by c.relname"

Check warning on line 243 in database/schema/grammars/postgres.go

View check run for this annotation

Codecov / codecov/patch

database/schema/grammars/postgres.go#L241-L243

Added lines #L241 - L243 were not covered by tests
}

func (r *Postgres) CompileTypes() string {
Expand Down Expand Up @@ -291,6 +304,15 @@
}

func (r *Postgres) ModifyDefault(blueprint schema.Blueprint, column schema.ColumnDefinition) string {
if column.IsChange() {
if column.GetAutoIncrement() {
return ""
}
if column.GetDefault() != nil {
return fmt.Sprintf(" set default %s", getDefaultValue(column.GetDefault()))
}
return " drop default"
}
if column.GetDefault() != nil {
return fmt.Sprintf(" default %s", getDefaultValue(column.GetDefault()))
}
Expand All @@ -299,15 +321,20 @@
}

func (r *Postgres) ModifyNullable(blueprint schema.Blueprint, column schema.ColumnDefinition) string {
almas1992 marked this conversation as resolved.
Show resolved Hide resolved
if column.IsChange() {
if column.GetNullable() {
return " drop not null"
}

Check warning on line 327 in database/schema/grammars/postgres.go

View check run for this annotation

Codecov / codecov/patch

database/schema/grammars/postgres.go#L326-L327

Added lines #L326 - L327 were not covered by tests
return " set not null"
}
if column.GetNullable() {
return " null"
} else {
return " not null"
}
return " not null"
}

func (r *Postgres) ModifyIncrement(blueprint schema.Blueprint, column schema.ColumnDefinition) string {
if !blueprint.HasCommand("primary") && slices.Contains(r.serials, column.GetType()) && column.GetAutoIncrement() {
if !column.IsChange() && !blueprint.HasCommand("primary") && slices.Contains(r.serials, column.GetType()) && column.GetAutoIncrement() {
return " primary key"
}

Expand Down
Loading
Loading