@@ -19,47 +19,59 @@ package migrationscripts
1919
2020import (
2121 "fmt"
22+ "strings"
23+
2224 "github.com/apache/incubator-devlake/core/context"
2325 "github.com/apache/incubator-devlake/core/dal"
2426 "github.com/apache/incubator-devlake/core/errors"
27+ "gorm.io/gorm/clause"
2528)
2629
2730type modifyCustomFieldName struct {}
2831
32+ type columnRename struct {
33+ Old string
34+ New string
35+ }
36+
2937func (* modifyCustomFieldName ) Up (basicRes context.BasicRes ) errors.Error {
3038 db := basicRes .GetDal ()
31- issuesNameList := []string {"_tool_tapd_stories" , "_tool_tapd_bugs" , "_tool_tapd_tasks" }
32- for _ , issuesName := range issuesNameList {
33- switch issuesName {
34- case "_tool_tapd_bugs" :
35- for i := 6 ; i < 9 ; i ++ {
36- oldColumnName := fmt .Sprintf ("custom_field%d" , i )
37- newColumnName := fmt .Sprintf ("custom_field_%d" , i )
38- if err := renameColumnSafely (db , issuesName , oldColumnName , newColumnName , dal .Text ); err != nil {
39- return err
40- }
41- }
42- case "_tool_tapd_tasks" , "_tool_tapd_stories" :
43- tableName := issuesName
44- renameColumnMap := map [string ]string {
45- "custom_field6" : "custom_field_six" ,
46- "custom_field7" : "custom_field_seven" ,
47- "custom_field8" : "custom_field_eight" ,
48- }
49- for oldColumn , newColumn := range renameColumnMap {
50- if err := renameColumnSafely (db , tableName , oldColumn , newColumn , dal .Text ); err != nil {
51- return err
52- }
53- }
39+
40+ // Define all column renames for each table
41+ tableRenames := map [string ][]columnRename {
42+ "_tool_tapd_bugs" : {
43+ {Old : "custom_field6" , New : "custom_field_6" },
44+ {Old : "custom_field7" , New : "custom_field_7" },
45+ {Old : "custom_field8" , New : "custom_field_8" },
46+ },
47+ "_tool_tapd_stories" : {
48+ {Old : "custom_field6" , New : "custom_field_six" },
49+ {Old : "custom_field7" , New : "custom_field_seven" },
50+ {Old : "custom_field8" , New : "custom_field_eight" },
51+ },
52+ "_tool_tapd_tasks" : {
53+ {Old : "custom_field6" , New : "custom_field_six" },
54+ {Old : "custom_field7" , New : "custom_field_seven" },
55+ {Old : "custom_field8" , New : "custom_field_eight" },
56+ },
57+ }
58+
59+ // Add custom_field_9 to custom_field_50 for all tables
60+ for i := 9 ; i <= 50 ; i ++ {
61+ oldCol := fmt .Sprintf ("custom_field%d" , i )
62+ newCol := fmt .Sprintf ("custom_field_%d" , i )
63+ for _ , table := range []string {"_tool_tapd_bugs" , "_tool_tapd_stories" , "_tool_tapd_tasks" } {
64+ tableRenames [table ] = append (tableRenames [table ], columnRename {Old : oldCol , New : newCol })
5465 }
55- for i := 9 ; i <= 50 ; i ++ {
56- oldColumnName := fmt . Sprintf ( "custom_field%d" , i )
57- newColumnName := fmt . Sprintf ( "custom_field_%d" , i )
58- if err := renameColumnSafely ( db , issuesName , oldColumnName , newColumnName , dal . Text ); err != nil {
59- return err
60- }
66+ }
67+
68+ // Execute batch rename for each table
69+ for tableName , renames := range tableRenames {
70+ if err := batchRenameColumns ( db , tableName , renames ); err != nil {
71+ return err
6172 }
6273 }
74+
6375 return nil
6476}
6577
@@ -71,18 +83,71 @@ func (*modifyCustomFieldName) Name() string {
7183 return "modify tapd custom field name"
7284}
7385
74- func renameColumnSafely (db dal.Dal , table , oldColumn string , newColumn string , newColumnType dal.ColumnType ) errors.Error {
75- if table == "" || oldColumn == "" || newColumn == "" {
76- return errors .BadInput .New ("empty params" )
86+ // batchRenameColumns renames multiple columns in a single ALTER TABLE statement
87+ func batchRenameColumns (db dal.Dal , table string , renames []columnRename ) errors.Error {
88+ if len (renames ) == 0 {
89+ return nil
7790 }
78- if db .HasColumn (table , oldColumn ) {
79- if ! db .HasColumn (table , newColumn ) {
80- return db .RenameColumn (table , oldColumn , newColumn )
91+
92+ // Get all existing column names once to avoid repeated information_schema queries
93+ existingColumns := getExistingColumns (db , table )
94+
95+ // Filter out renames where old column doesn't exist or new column already exists
96+ validRenames := filterValidRenamesCached (renames , existingColumns )
97+ if len (validRenames ) == 0 {
98+ return nil
99+ }
100+
101+ var sql string
102+ var dialect = db .Dialect ()
103+
104+ if dialect == "postgres" {
105+ // PostgreSQL requires separate ALTER TABLE statements for each RENAME COLUMN
106+ for _ , rename := range validRenames {
107+ sql = fmt .Sprintf (`ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"` , table , rename .Old , rename .New )
108+ if err := db .Exec (sql ); err != nil {
109+ return err
110+ }
81111 }
112+ // Clear PostgreSQL cached plan after all renames
113+ _ = db .Exec ("SELECT * FROM ? LIMIT 1" , clause.Table {Name : table })
82114 } else {
83- if ! db .HasColumn (table , newColumn ) {
84- return db .AddColumn (table , newColumn , newColumnType )
115+ // MySQL: ALTER TABLE t CHANGE COLUMN a new_name TEXT, CHANGE COLUMN c new_name2 TEXT
116+ clauses := make ([]string , 0 , len (validRenames ))
117+ for _ , rename := range validRenames {
118+ clauses = append (clauses , fmt .Sprintf ("CHANGE COLUMN `%s` `%s` %s" , rename .Old , rename .New , dal .Text .String ()))
119+ }
120+ sql = fmt .Sprintf ("ALTER TABLE `%s` %s" , table , strings .Join (clauses , ", " ))
121+ if err := db .Exec (sql ); err != nil {
122+ return err
85123 }
86124 }
125+
87126 return nil
88127}
128+
129+ // getExistingColumns fetches all column names for a table in a single query
130+ func getExistingColumns (db dal.Dal , table string ) map [string ]bool {
131+ columns := make (map [string ]bool )
132+ columnMetas , err := db .GetColumns (& dal.DefaultTabler {Name : table }, nil )
133+ if err != nil {
134+ return columns
135+ }
136+ for _ , col := range columnMetas {
137+ columns [col .Name ()] = true
138+ }
139+ return columns
140+ }
141+
142+ // filterValidRenamesCached checks which renames are needed using pre-fetched column map
143+ func filterValidRenamesCached (renames []columnRename , existingColumns map [string ]bool ) []columnRename {
144+ valid := make ([]columnRename , 0 , len (renames ))
145+ for _ , rename := range renames {
146+ oldExists := existingColumns [rename .Old ]
147+ newExists := existingColumns [rename .New ]
148+ if oldExists && ! newExists {
149+ valid = append (valid , rename )
150+ }
151+ }
152+ return valid
153+ }
0 commit comments