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:
silverwind
2026-04-27 13:00:47 +02:00
committed by beardev-in
parent d6ae7c1c50
commit d18464fd58
2 changed files with 20 additions and 31 deletions

View File

@@ -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
} }

View File

@@ -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)
} }