55 "database/sql"
66 "errors"
77 "fmt"
8+ "regexp"
89 "strconv"
910 "strings"
1011
@@ -26,6 +27,49 @@ type Migrator struct {
2627 Dialector
2728}
2829
30+ // isolateClusterOption splits ON CLUSTER clause from raw table options
31+ func isolateClusterOption (tableOpts string ) (clusterOpts , cleanedOpts string ) {
32+ cleanedOpts = tableOpts
33+ if tableOpts == "" {
34+ return
35+ }
36+ re := regexp .MustCompile (`ON CLUSTER (?:'([^']+)'|([^\s]+))` )
37+ clusterMatch := re .FindString (tableOpts )
38+ if clusterMatch == "" {
39+ return
40+ }
41+ clusterOpts = formatClusterClause (clusterMatch )
42+ cleanedOpts = strings .TrimSpace (strings .Replace (tableOpts , clusterMatch , "" , 1 ))
43+ return
44+ }
45+
46+ // formatClusterClause ensures ON CLUSTER clause begins with a single space and has no trailing space
47+ func formatClusterClause (cluster string ) string {
48+ clause := strings .TrimSpace (cluster )
49+ if clause == "" {
50+ return ""
51+ }
52+ if ! strings .HasPrefix (clause , " " ) {
53+ clause = " " + clause
54+ }
55+ return clause
56+ }
57+
58+ // extractClusterOption extracts ON CLUSTER clause from table options
59+ func (m Migrator ) extractClusterOption () string {
60+ // Extract ON CLUSTER from gorm:table_options
61+ if tableOption , ok := m .DB .Get ("gorm:table_options" ); ok {
62+ if clusterOpts , _ := isolateClusterOption (fmt .Sprint (tableOption )); clusterOpts != "" {
63+ return clusterOpts
64+ }
65+ }
66+ // Also support legacy gorm:table_cluster_options (for backward compatibility)
67+ if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
68+ return formatClusterClause (fmt .Sprint (clusterOption ))
69+ }
70+ return ""
71+ }
72+
2973// Database
3074
3175func (m Migrator ) CurrentDatabase () (name string ) {
@@ -85,7 +129,7 @@ func (m Migrator) CreateTable(models ...interface{}) error {
85129 tx := m .DB .Session (new (gorm.Session ))
86130 if err := m .RunWithValue (model , func (stmt * gorm.Statement ) (err error ) {
87131 var (
88- createTableSQL = "CREATE TABLE ?%s(%s %s %s) %s"
132+ createTableSQL = "CREATE TABLE ?%s (%s %s %s) %s"
89133 args = []interface {}{clause.Table {Name : stmt .Table }}
90134 )
91135
@@ -156,13 +200,20 @@ func (m Migrator) CreateTable(models ...interface{}) error {
156200
157201 // Step 4. Finally assemble CREATE TABLE ... SQL string
158202 engineOpts := m .Dialector .DefaultTableEngineOpts
159- if tableOption , ok := m .DB .Get ("gorm:table_options" ); ok {
160- engineOpts = fmt .Sprint (tableOption )
203+ tableOption , hasTableOption := m .DB .Get ("gorm:table_options" )
204+ clusterOpts := ""
205+ if hasTableOption {
206+ tableOpts := fmt .Sprint (tableOption )
207+ var cleanedOpts string
208+ clusterOpts , cleanedOpts = isolateClusterOption (tableOpts )
209+ engineOpts = cleanedOpts
161210 }
162211
163- clusterOpts := ""
212+ // Also support legacy gorm:table_cluster_options (for backward compatibility)
164213 if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
165- clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
214+ if clusterOpts == "" {
215+ clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
216+ }
166217 }
167218
168219 createTableSQL = fmt .Sprintf (createTableSQL , clusterOpts , columnStr , constrStr , indexStr , engineOpts )
@@ -218,11 +269,8 @@ func (m Migrator) GetTables() (tableList []string, err error) {
218269func (m Migrator ) AddColumn (value interface {}, field string ) error {
219270 return m .RunWithValue (value , func (stmt * gorm.Statement ) error {
220271 if field := stmt .Schema .LookUpField (field ); field != nil {
221- clusterOpts := ""
222- if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
223- clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
224- }
225- sQL := fmt .Sprintf ("ALTER TABLE ? %s ADD COLUMN ? ?" , clusterOpts )
272+ clusterOpts := m .extractClusterOption ()
273+ sQL := fmt .Sprintf ("ALTER TABLE ?%s ADD COLUMN ? ?" , clusterOpts )
226274 return m .DB .Exec (
227275 sQL ,
228276 clause.Table {Name : stmt .Table }, clause.Column {Name : field .DBName },
@@ -238,11 +286,8 @@ func (m Migrator) DropColumn(value interface{}, name string) error {
238286 if field := stmt .Schema .LookUpField (name ); field != nil {
239287 name = field .DBName
240288 }
241- clusterOpts := ""
242- if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
243- clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
244- }
245- sQL := fmt .Sprintf ("ALTER TABLE ? %s DROP COLUMN ?" , clusterOpts )
289+ clusterOpts := m .extractClusterOption ()
290+ sQL := fmt .Sprintf ("ALTER TABLE ?%s DROP COLUMN ?" , clusterOpts )
246291 return m .DB .Exec (
247292 sQL ,
248293 clause.Table {Name : stmt .Table }, clause.Column {Name : name },
@@ -253,11 +298,8 @@ func (m Migrator) DropColumn(value interface{}, name string) error {
253298func (m Migrator ) AlterColumn (value interface {}, field string ) error {
254299 return m .RunWithValue (value , func (stmt * gorm.Statement ) error {
255300 if field := stmt .Schema .LookUpField (field ); field != nil {
256- clusterOpts := ""
257- if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
258- clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
259- }
260- sQL := fmt .Sprintf ("ALTER TABLE ? %s MODIFY COLUMN ? ?" , clusterOpts )
301+ clusterOpts := m .extractClusterOption ()
302+ sQL := fmt .Sprintf ("ALTER TABLE ?%s MODIFY COLUMN ? ?" , clusterOpts )
261303 return m .DB .Exec (
262304 sQL ,
263305 clause.Table {Name : stmt .Table },
@@ -284,11 +326,8 @@ func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error
284326 field = f
285327 }
286328 if field != nil {
287- clusterOpts := ""
288- if clusterOption , ok := m .DB .Get ("gorm:table_cluster_options" ); ok {
289- clusterOpts = " " + fmt .Sprint (clusterOption ) + " "
290- }
291- sQL := fmt .Sprintf ("ALTER TABLE ? %s RENAME COLUMN ? TO ?" , clusterOpts )
329+ clusterOpts := m .extractClusterOption ()
330+ sQL := fmt .Sprintf ("ALTER TABLE ?%s RENAME COLUMN ? TO ?" , clusterOpts )
292331 return m .DB .Exec (
293332 sQL ,
294333 clause.Table {Name : stmt .Table },
0 commit comments