fix decimal migrate error.(#7450)
Signed-off-by: Chise1 <chise123@live.com>
This commit is contained in:
		
							parent
							
								
									e5b867e785
								
							
						
					
					
						commit
						fa8072bafc
					
				| @ -474,7 +474,6 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy | |||||||
| 	// found, smart migrate
 | 	// found, smart migrate
 | ||||||
| 	fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL)) | 	fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL)) | ||||||
| 	realDataType := strings.ToLower(columnType.DatabaseTypeName()) | 	realDataType := strings.ToLower(columnType.DatabaseTypeName()) | ||||||
| 
 |  | ||||||
| 	var ( | 	var ( | ||||||
| 		alterColumn bool | 		alterColumn bool | ||||||
| 		isSameType  = fullDataType == realDataType | 		isSameType  = fullDataType == realDataType | ||||||
| @ -513,8 +512,19 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 		// check precision
 | 	// check precision
 | ||||||
|  | 	if realDataType == "decimal" || realDataType == "numeric" && | ||||||
|  | 		regexp.MustCompile(realDataType+`\(.*\)`).FindString(fullDataType) != "" { // if realDataType has no precision,ignore
 | ||||||
|  | 		precision, scale, ok := columnType.DecimalSize() | ||||||
|  | 		if ok { | ||||||
|  | 			if !strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d,%d)", realDataType, precision, scale)) && | ||||||
|  | 				!strings.HasPrefix(fullDataType, fmt.Sprintf("%s(%d)", realDataType, precision)) { | ||||||
|  | 				alterColumn = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
| 		if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision { | 		if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision { | ||||||
| 			if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) { | 			if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) { | ||||||
| 				alterColumn = true | 				alterColumn = true | ||||||
|  | |||||||
| @ -12,8 +12,8 @@ require ( | |||||||
| 	gorm.io/driver/mysql v1.5.7 | 	gorm.io/driver/mysql v1.5.7 | ||||||
| 	gorm.io/driver/postgres v1.5.11 | 	gorm.io/driver/postgres v1.5.11 | ||||||
| 	gorm.io/driver/sqlite v1.5.7 | 	gorm.io/driver/sqlite v1.5.7 | ||||||
| 	gorm.io/driver/sqlserver v1.5.4 | 	gorm.io/driver/sqlserver v1.6.0 | ||||||
| 	gorm.io/gorm v1.25.12 | 	gorm.io/gorm v1.30.0 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| @ -24,11 +24,11 @@ require ( | |||||||
| 	github.com/golang-sql/sqlexp v0.1.0 // indirect | 	github.com/golang-sql/sqlexp v0.1.0 // indirect | ||||||
| 	github.com/jackc/pgpassfile v1.0.0 // indirect | 	github.com/jackc/pgpassfile v1.0.0 // indirect | ||||||
| 	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect | 	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect | ||||||
| 	github.com/jackc/pgx/v5 v5.7.1 // indirect | 	github.com/jackc/pgx/v5 v5.7.5 // indirect | ||||||
| 	github.com/jinzhu/inflection v1.0.0 // indirect | 	github.com/jinzhu/inflection v1.0.0 // indirect | ||||||
| 	github.com/kr/text v0.2.0 // indirect | 	github.com/kr/text v0.2.0 // indirect | ||||||
| 	github.com/mattn/go-sqlite3 v1.14.28 // indirect | 	github.com/mattn/go-sqlite3 v1.14.28 // indirect | ||||||
| 	github.com/microsoft/go-mssqldb v1.7.2 // indirect | 	github.com/microsoft/go-mssqldb v1.8.1 // indirect | ||||||
| 	github.com/pmezard/go-difflib v1.0.0 // indirect | 	github.com/pmezard/go-difflib v1.0.0 // indirect | ||||||
| 	github.com/rogpeppe/go-internal v1.12.0 // indirect | 	github.com/rogpeppe/go-internal v1.12.0 // indirect | ||||||
| 	golang.org/x/crypto v0.38.0 // indirect | 	golang.org/x/crypto v0.38.0 // indirect | ||||||
|  | |||||||
| @ -1987,3 +1987,114 @@ func TestMigrateWithUniqueIndexAndUnique(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func testAutoMigrateDecimal(t *testing.T, model1, model2 any) []string { | ||||||
|  | 	tracer := Tracer{ | ||||||
|  | 		Logger: DB.Config.Logger, | ||||||
|  | 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||||||
|  | 			sql, _ := fc() | ||||||
|  | 			if strings.HasPrefix(sql, "ALTER TABLE ") { | ||||||
|  | 				t.Fatalf("shouldn't execute ALTER COLUMN TYPE if decimal is not change: sql: %s", sql) | ||||||
|  | 			} | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	session := DB.Session(&gorm.Session{Logger: tracer}) | ||||||
|  | 
 | ||||||
|  | 	DB.Migrator().DropTable(model1) | ||||||
|  | 	var modifySql []string | ||||||
|  | 	if err := session.AutoMigrate(model1); err != nil { | ||||||
|  | 		t.Fatalf("failed to auto migrate, got error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	if err := session.AutoMigrate(model1); err != nil { | ||||||
|  | 		t.Fatalf("failed to auto migrate, got error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	tracer2 := Tracer{ | ||||||
|  | 		Logger: DB.Config.Logger, | ||||||
|  | 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||||||
|  | 			sql, _ := fc() | ||||||
|  | 			modifySql = append(modifySql, sql) | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	session2 := DB.Session(&gorm.Session{Logger: tracer2}) | ||||||
|  | 	err := session2.Table("migrate_decimal_columns").Migrator().AutoMigrate(model2) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("failed to get column types, got error: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return modifySql | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func decimalColumnsTest[T, T2 any](t *testing.T, expectedSql []string) { | ||||||
|  | 	var t1 T | ||||||
|  | 	var t2 T2 | ||||||
|  | 	modSql := testAutoMigrateDecimal(t, t1, t2) | ||||||
|  | 	var alterSQL []string | ||||||
|  | 	for _, sql := range modSql { | ||||||
|  | 		if strings.HasPrefix(sql, "ALTER TABLE ") { | ||||||
|  | 			alterSQL = append(alterSQL, sql) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(alterSQL) != 3 { | ||||||
|  | 		t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL) | ||||||
|  | 	} | ||||||
|  | 	for i := range alterSQL { | ||||||
|  | 		if alterSQL[i] != expectedSql[i] { | ||||||
|  | 			t.Fatalf("decimal changed error,expected: %+v,got: %+v.", expectedSql, alterSQL) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestAutoMigrateDecimal(t *testing.T) { | ||||||
|  | 	if DB.Dialector.Name() == "sqlserver" { // database/sql will replace numeric to decimal. so only support decimal.
 | ||||||
|  | 		type MigrateDecimalColumn struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		type MigrateDecimalColumn2 struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		expectedSql := []string{ | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" decimal(8) NOT NULL`, | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" decimal(9,1) NOT NULL`, | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" decimal(9,2) NOT NULL`, | ||||||
|  | 		} | ||||||
|  | 		decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) | ||||||
|  | 	} else if DB.Dialector.Name() == "postgres" { | ||||||
|  | 		type MigrateDecimalColumn struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:numeric(9,0);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:numeric(8);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:numeric(8,1);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		type MigrateDecimalColumn2 struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:numeric(8);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:numeric(9,1);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:numeric(9,2);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		expectedSql := []string{ | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid1" TYPE numeric(8) USING "recid1"::numeric(8)`, | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid2" TYPE numeric(9,1) USING "recid2"::numeric(9,1)`, | ||||||
|  | 			`ALTER TABLE "migrate_decimal_columns" ALTER COLUMN "recid3" TYPE numeric(9,2) USING "recid3"::numeric(9,2)`, | ||||||
|  | 		} | ||||||
|  | 		decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) | ||||||
|  | 	} else if DB.Dialector.Name() == "mysql" { | ||||||
|  | 		type MigrateDecimalColumn struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:decimal(9,0);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:decimal(8);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:decimal(8,1);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		type MigrateDecimalColumn2 struct { | ||||||
|  | 			RecID1 int64 `gorm:"column:recid1;type:decimal(8);not null" json:"recid1"` | ||||||
|  | 			RecID2 int64 `gorm:"column:recid2;type:decimal(9,1);not null" json:"recid2"` | ||||||
|  | 			RecID3 int64 `gorm:"column:recid3;type:decimal(9,2);not null" json:"recid3"` | ||||||
|  | 		} | ||||||
|  | 		expectedSql := []string{ | ||||||
|  | 			"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid1` decimal(8) NOT NULL", | ||||||
|  | 			"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid2` decimal(9,1) NOT NULL", | ||||||
|  | 			"ALTER TABLE `migrate_decimal_columns` MODIFY COLUMN `recid3` decimal(9,2) NOT NULL", | ||||||
|  | 		} | ||||||
|  | 		decimalColumnsTest[MigrateDecimalColumn, MigrateDecimalColumn2](t, expectedSql) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Chise1
						Chise1