Skip to content

Commit eae4622

Browse files
committed
fix decimal migrate error.(#7450)
Signed-off-by: Chise1 <[email protected]>
1 parent e5b867e commit eae4622

File tree

3 files changed

+124
-5
lines changed

3 files changed

+124
-5
lines changed

migrator/migrator.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,6 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
474474
// found, smart migrate
475475
fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))
476476
realDataType := strings.ToLower(columnType.DatabaseTypeName())
477-
478477
var (
479478
alterColumn bool
480479
isSameType = fullDataType == realDataType
@@ -513,8 +512,19 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
513512
}
514513
}
515514
}
515+
}
516516

517-
// check precision
517+
// check precision
518+
if realDataType == "decimal" || realDataType == "numeric" &&
519+
regexp.MustCompile(realDataType+`\(.*\)`).FindString(fullDataType) != "" { // if realDataType has no precision,ignore
520+
precision, scale, ok := columnType.DecimalSize()
521+
if ok {
522+
if !strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d,%d)", realDataType, precision, scale)) &&
523+
!strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d)", realDataType, precision)) {
524+
alterColumn = true
525+
}
526+
}
527+
} else {
518528
if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
519529
if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
520530
alterColumn = true

tests/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ require (
1313
gorm.io/driver/postgres v1.5.11
1414
gorm.io/driver/sqlite v1.5.7
1515
gorm.io/driver/sqlserver v1.5.4
16-
gorm.io/gorm v1.25.12
16+
gorm.io/gorm v1.26.1
1717
)
1818

1919
require (
@@ -24,11 +24,11 @@ require (
2424
github.com/golang-sql/sqlexp v0.1.0 // indirect
2525
github.com/jackc/pgpassfile v1.0.0 // indirect
2626
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
27-
github.com/jackc/pgx/v5 v5.7.1 // indirect
27+
github.com/jackc/pgx/v5 v5.7.5 // indirect
2828
github.com/jinzhu/inflection v1.0.0 // indirect
2929
github.com/kr/text v0.2.0 // indirect
3030
github.com/mattn/go-sqlite3 v1.14.28 // indirect
31-
github.com/microsoft/go-mssqldb v1.7.2 // indirect
31+
github.com/microsoft/go-mssqldb v1.8.1 // indirect
3232
github.com/pmezard/go-difflib v1.0.0 // indirect
3333
github.com/rogpeppe/go-internal v1.12.0 // indirect
3434
golang.org/x/crypto v0.38.0 // indirect

tests/migrate_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1987,3 +1987,112 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
19871987
}
19881988
}
19891989
}
1990+
1991+
func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string {
1992+
tracer := Tracer{
1993+
Logger: DB.Config.Logger,
1994+
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
1995+
sql, _ := fc()
1996+
if strings.HasPrefix(sql, "ALTER TABLE ") {
1997+
t.Fatalf("shouldn't execute ALTER COLUMN TYPE if decimal is not change: sql: %s", sql)
1998+
}
1999+
},
2000+
}
2001+
session := DB.Session(&gorm.Session{Logger: tracer})
2002+
2003+
DB.Migrator().DropTable(model1)
2004+
var modifySql []string
2005+
if err := session.AutoMigrate(model1); err != nil {
2006+
t.Fatalf("failed to auto migrate, got error: %v", err)
2007+
}
2008+
if err := session.AutoMigrate(model1); err != nil {
2009+
t.Fatalf("failed to auto migrate, got error: %v", err)
2010+
}
2011+
tracer2 := Tracer{
2012+
Logger: DB.Config.Logger,
2013+
Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
2014+
sql, _ := fc()
2015+
modifySql = append(modifySql, sql)
2016+
},
2017+
}
2018+
session2 := DB.Session(&gorm.Session{Logger: tracer2})
2019+
err := session2.Table("migrate_decimal_columns").Migrator().AutoMigrate(model2)
2020+
if err != nil {
2021+
t.Fatalf("failed to get column types, got error: %v", err)
2022+
}
2023+
return modifySql
2024+
}
2025+
func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) {
2026+
var t1 T
2027+
var t2 T2
2028+
modSql := testAutoMigrateDecimal(t, t1, t2)
2029+
var alterSQL []string
2030+
for _, sql := range modSql {
2031+
if strings.HasPrefix(sql, "ALTER TABLE ") {
2032+
alterSQL = append(alterSQL, sql)
2033+
}
2034+
}
2035+
2036+
if len(alterSQL) != 3 {
2037+
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
2038+
}
2039+
for i := range alterSQL {
2040+
if alterSQL[i] != expectedSql[i] {
2041+
t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL)
2042+
}
2043+
}
2044+
}
2045+
func TestAutoMigrateDecimal(t *testing.T) {
2046+
if DB.Dialector.Name() == "sqlserver" { // database/sql will replace numeric to decimal. so only support decimal.
2047+
type MigrateDecimalColumn struct {
2048+
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
2049+
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
2050+
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
2051+
}
2052+
type MigrateDecimalColumn2 struct {
2053+
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
2054+
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
2055+
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
2056+
}
2057+
expectedSql := []string{
2058+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" decimal(8) NOT NULL`,
2059+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" decimal(9,1) NOT NULL`,
2060+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`,
2061+
}
2062+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
2063+
} else if DB.Dialector.Name() == "postgres" {
2064+
type MigrateDecimalColumn struct {
2065+
RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"`
2066+
RecID2 int64 `gorm:"column:recid2;type:numeric(8);not null" json:"recid2"`
2067+
RecID3 int64 `gorm:"column:recid3;type:numeric(8,1);not null" json:"recid3"`
2068+
}
2069+
type MigrateDecimalColumn2 struct {
2070+
RecID1 int64 `gorm:"column:recid1;type:numeric(8);not null" json:"recid1"`
2071+
RecID2 int64 `gorm:"column:recid2;type:numeric(9,1);not null" json:"recid2"`
2072+
RecID3 int64 `gorm:"column:recid3;type:numeric(9,2);not null" json:"recid3"`
2073+
}
2074+
expectedSql := []string{
2075+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" TYPE numeric(8) USING "recid1"::numeric(8)`,
2076+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" TYPE numeric(9,1) USING "recid2"::numeric(9,1)`,
2077+
`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" TYPE numeric(9,2) USING "recid3"::numeric(9,2)`,
2078+
}
2079+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
2080+
} else if DB.Dialector.Name() == "mysql" {
2081+
type MigrateDecimalColumn struct {
2082+
RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"`
2083+
RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"`
2084+
RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"`
2085+
}
2086+
type MigrateDecimalColumn2 struct {
2087+
RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"`
2088+
RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"`
2089+
RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"`
2090+
}
2091+
expectedSql := []string{
2092+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL",
2093+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL",
2094+
"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL",
2095+
}
2096+
decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql)
2097+
}
2098+
}

0 commit comments

Comments
 (0)