Tighten v332 migration cleanup pass
- Use setting.Database.Type.IsSQLite3() / IsMySQL() for dialect checks to match the rest of models/migrations/. - Trim the migration's comment to the load-bearing why (MSSQL rejects inline DEFAULT, MySQL drops it on MODIFY COLUMN), drop the discovery narrative. - Trim test comments and tighten the type-name assertion list to the values dialects actually emit (verified empirically against the CI image versions: MySQL/MSSQL report "INT", Postgres reports "INTEGER"; "INT4" never surfaces, removed). Re-tested against postgres:14, bitnamilegacy/mysql:8.0, mssql:2019-latest, and SQLite — all pass. Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -5,23 +5,20 @@ package v1_27
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models/migrations/base"
|
"code.gitea.io/gitea/models/migrations/base"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
"xorm.io/xorm/schemas"
|
"xorm.io/xorm/schemas"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WidenProjectBoardSorting changes project_board.sorting from int8 (TINYINT/SMALLINT)
|
// WidenProjectBoardSorting changes project_board.sorting from int8 to int so the
|
||||||
// to int. The previous int8 type capped projects at 127 columns and forced the API
|
// API can stop truncating sort values and the column count is no longer capped at
|
||||||
// to truncate user-supplied sort values. SQLite uses dynamic typing so the schema
|
// 127. DefaultIsEmpty: true is required because MSSQL's ALTER COLUMN rejects an
|
||||||
// type is cosmetic; existing rows already store the wider value.
|
// inline DEFAULT, and MySQL's MODIFY COLUMN drops any DEFAULT not restated, so the
|
||||||
//
|
// default is reapplied for MySQL afterwards. Postgres and MSSQL keep the existing
|
||||||
// `base.ModifyColumn` is called with DefaultIsEmpty: true because MSSQL's ALTER
|
// DEFAULT constraint independently of the type change.
|
||||||
// COLUMN syntax does not accept inline DEFAULT (it would error on the keyword).
|
|
||||||
// On MySQL, MODIFY COLUMN without an explicit DEFAULT drops the existing default,
|
|
||||||
// so it has to be reapplied. On Postgres and MSSQL the DEFAULT constraint is
|
|
||||||
// maintained separately from the column type and is preserved automatically.
|
|
||||||
func WidenProjectBoardSorting(x *xorm.Engine) error {
|
func WidenProjectBoardSorting(x *xorm.Engine) error {
|
||||||
if x.Dialect().URI().DBType == schemas.SQLITE {
|
if setting.Database.Type.IsSQLite3() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := base.ModifyColumn(x, "project_board", &schemas.Column{
|
if err := base.ModifyColumn(x, "project_board", &schemas.Column{
|
||||||
@@ -32,7 +29,7 @@ func WidenProjectBoardSorting(x *xorm.Engine) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if x.Dialect().URI().DBType == schemas.MYSQL {
|
if setting.Database.Type.IsMySQL() {
|
||||||
if _, err := x.Exec("ALTER TABLE `project_board` ALTER `sorting` SET DEFAULT 0"); err != nil {
|
if _, err := x.Exec("ALTER TABLE `project_board` ALTER `sorting` SET DEFAULT 0"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_WidenProjectBoardSorting(t *testing.T) {
|
func Test_WidenProjectBoardSorting(t *testing.T) {
|
||||||
// Pre-migration shape of project_board (only the column we care about plus the
|
// Pre-migration shape: int8 sorting column.
|
||||||
// minimum needed to pass NOT NULL constraints during INSERT).
|
|
||||||
type projectBoard struct {
|
type projectBoard struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Title string
|
Title string
|
||||||
@@ -27,34 +26,28 @@ func Test_WidenProjectBoardSorting(t *testing.T) {
|
|||||||
x, deferrable := base.PrepareTestEnv(t, 0, new(projectBoard))
|
x, deferrable := base.PrepareTestEnv(t, 0, new(projectBoard))
|
||||||
defer deferrable()
|
defer deferrable()
|
||||||
|
|
||||||
// Seed two rows: one at the int8 lower bound and one at the upper bound,
|
|
||||||
// proving the migration preserves edge values without truncation.
|
|
||||||
_, err := x.Insert(
|
_, err := x.Insert(
|
||||||
&projectBoard{Title: "first", Sorting: 0, ProjectID: 1, CreatorID: 1},
|
&projectBoard{Title: "first", Sorting: 0, ProjectID: 1, CreatorID: 1},
|
||||||
&projectBoard{Title: "boundary", Sorting: 127, ProjectID: 1, CreatorID: 1},
|
&projectBoard{Title: "boundary", Sorting: 127, ProjectID: 1, CreatorID: 1}, // int8 max
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.NoError(t, WidenProjectBoardSorting(x))
|
require.NoError(t, WidenProjectBoardSorting(x))
|
||||||
|
|
||||||
// Verify column type widened (skipped on SQLite where the migration is a no-op).
|
// SQLite uses dynamic typing so the schema metadata still reports the original
|
||||||
|
// declared type; only verify schema metadata on real RDBMSes.
|
||||||
if !setting.Database.Type.IsSQLite3() {
|
if !setting.Database.Type.IsSQLite3() {
|
||||||
table := base.LoadTableSchemasMap(t, x)["project_board"]
|
table := base.LoadTableSchemasMap(t, x)["project_board"]
|
||||||
require.NotNil(t, table)
|
require.NotNil(t, table)
|
||||||
col := table.GetColumn("sorting")
|
col := table.GetColumn("sorting")
|
||||||
require.NotNil(t, col)
|
require.NotNil(t, col)
|
||||||
// Each dialect spells INT differently; verify the type is one of the wider
|
// MySQL and MSSQL report "INT", Postgres reports "INTEGER".
|
||||||
// names rather than TINYINT/INT2.
|
assert.Contains(t, []string{"INT", "INTEGER"}, col.SQLType.Name)
|
||||||
assert.Contains(t,
|
assert.False(t, col.Nullable)
|
||||||
[]string{"INT", "INTEGER", "INT4"},
|
assert.Equal(t, "0", col.Default)
|
||||||
col.SQLType.Name,
|
|
||||||
"sorting column should have widened to int",
|
|
||||||
)
|
|
||||||
assert.False(t, col.Nullable, "sorting column should remain NOT NULL")
|
|
||||||
assert.Equal(t, "0", col.Default, "sorting column should keep DEFAULT 0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Existing rows must be preserved verbatim.
|
// Post-migration shape: same table, int sorting.
|
||||||
type projectBoardWide struct {
|
type projectBoardWide struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
Title string
|
Title string
|
||||||
@@ -68,8 +61,7 @@ func Test_WidenProjectBoardSorting(t *testing.T) {
|
|||||||
assert.Equal(t, 0, rows[0].Sorting)
|
assert.Equal(t, 0, rows[0].Sorting)
|
||||||
assert.Equal(t, 127, rows[1].Sorting)
|
assert.Equal(t, 127, rows[1].Sorting)
|
||||||
|
|
||||||
// Inserting a value > 127 must succeed after widening (would have failed with
|
// Value well past int8 range — proves the column genuinely widened.
|
||||||
// TINYINT/INT2 either by truncation or out-of-range error).
|
|
||||||
_, err = x.Table("project_board").Insert(&projectBoardWide{
|
_, err = x.Table("project_board").Insert(&projectBoardWide{
|
||||||
Title: "wide",
|
Title: "wide",
|
||||||
Sorting: 30000,
|
Sorting: 30000,
|
||||||
@@ -82,5 +74,5 @@ func Test_WidenProjectBoardSorting(t *testing.T) {
|
|||||||
has, err := x.Table("project_board").Where("title=?", "wide").Get(&got)
|
has, err := x.Table("project_board").Where("title=?", "wide").Get(&got)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, has)
|
require.True(t, has)
|
||||||
assert.Equal(t, 30000, got.Sorting, "value should round-trip without truncation")
|
assert.Equal(t, 30000, got.Sorting)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user