migrator.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. package migrator
  2. import (
  3. "context"
  4. "fmt"
  5. "reflect"
  6. "regexp"
  7. "strings"
  8. "gorm.io/gorm"
  9. "gorm.io/gorm/clause"
  10. "gorm.io/gorm/schema"
  11. )
  12. // Migrator m struct
  13. type Migrator struct {
  14. Config
  15. }
  16. // Config schema config
  17. type Config struct {
  18. CreateIndexAfterCreateTable bool
  19. DB *gorm.DB
  20. gorm.Dialector
  21. }
  22. type GormDataTypeInterface interface {
  23. GormDBDataType(*gorm.DB, *schema.Field) string
  24. }
  25. func (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statement) error) error {
  26. stmt := &gorm.Statement{DB: m.DB}
  27. if m.DB.Statement != nil {
  28. stmt.Table = m.DB.Statement.Table
  29. stmt.TableExpr = m.DB.Statement.TableExpr
  30. }
  31. if table, ok := value.(string); ok {
  32. stmt.Table = table
  33. } else if err := stmt.Parse(value); err != nil {
  34. return err
  35. }
  36. return fc(stmt)
  37. }
  38. func (m Migrator) DataTypeOf(field *schema.Field) string {
  39. fieldValue := reflect.New(field.IndirectFieldType)
  40. if dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok {
  41. if dataType := dataTyper.GormDBDataType(m.DB, field); dataType != "" {
  42. return dataType
  43. }
  44. }
  45. return m.Dialector.DataTypeOf(field)
  46. }
  47. func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
  48. expr.SQL = m.DataTypeOf(field)
  49. if field.NotNull {
  50. expr.SQL += " NOT NULL"
  51. }
  52. if field.Unique {
  53. expr.SQL += " UNIQUE"
  54. }
  55. if field.HasDefaultValue && (field.DefaultValueInterface != nil || field.DefaultValue != "") {
  56. if field.DefaultValueInterface != nil {
  57. defaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValueInterface}}
  58. m.Dialector.BindVarTo(defaultStmt, defaultStmt, field.DefaultValueInterface)
  59. expr.SQL += " DEFAULT " + m.Dialector.Explain(defaultStmt.SQL.String(), field.DefaultValueInterface)
  60. } else if field.DefaultValue != "(-)" {
  61. expr.SQL += " DEFAULT " + field.DefaultValue
  62. }
  63. }
  64. return
  65. }
  66. // AutoMigrate
  67. func (m Migrator) AutoMigrate(values ...interface{}) error {
  68. for _, value := range m.ReorderModels(values, true) {
  69. tx := m.DB.Session(&gorm.Session{})
  70. if !tx.Migrator().HasTable(value) {
  71. if err := tx.Migrator().CreateTable(value); err != nil {
  72. return err
  73. }
  74. } else {
  75. if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
  76. columnTypes, _ := m.DB.Migrator().ColumnTypes(value)
  77. for _, field := range stmt.Schema.FieldsByDBName {
  78. var foundColumn gorm.ColumnType
  79. for _, columnType := range columnTypes {
  80. if columnType.Name() == field.DBName {
  81. foundColumn = columnType
  82. break
  83. }
  84. }
  85. if foundColumn == nil {
  86. // not found, add column
  87. if err := tx.Migrator().AddColumn(value, field.DBName); err != nil {
  88. return err
  89. }
  90. } else if err := m.DB.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
  91. // found, smart migrate
  92. return err
  93. }
  94. }
  95. for _, rel := range stmt.Schema.Relationships.Relations {
  96. if !m.DB.Config.DisableForeignKeyConstraintWhenMigrating {
  97. if constraint := rel.ParseConstraint(); constraint != nil {
  98. if constraint.Schema == stmt.Schema {
  99. if !tx.Migrator().HasConstraint(value, constraint.Name) {
  100. if err := tx.Migrator().CreateConstraint(value, constraint.Name); err != nil {
  101. return err
  102. }
  103. }
  104. }
  105. }
  106. }
  107. for _, chk := range stmt.Schema.ParseCheckConstraints() {
  108. if !tx.Migrator().HasConstraint(value, chk.Name) {
  109. if err := tx.Migrator().CreateConstraint(value, chk.Name); err != nil {
  110. return err
  111. }
  112. }
  113. }
  114. }
  115. for _, idx := range stmt.Schema.ParseIndexes() {
  116. if !tx.Migrator().HasIndex(value, idx.Name) {
  117. if err := tx.Migrator().CreateIndex(value, idx.Name); err != nil {
  118. return err
  119. }
  120. }
  121. }
  122. return nil
  123. }); err != nil {
  124. return err
  125. }
  126. }
  127. }
  128. return nil
  129. }
  130. func (m Migrator) CreateTable(values ...interface{}) error {
  131. for _, value := range m.ReorderModels(values, false) {
  132. tx := m.DB.Session(&gorm.Session{})
  133. if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
  134. var (
  135. createTableSQL = "CREATE TABLE ? ("
  136. values = []interface{}{m.CurrentTable(stmt)}
  137. hasPrimaryKeyInDataType bool
  138. )
  139. for _, dbName := range stmt.Schema.DBNames {
  140. field := stmt.Schema.FieldsByDBName[dbName]
  141. createTableSQL += "? ?"
  142. hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(string(field.DataType)), "PRIMARY KEY")
  143. values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))
  144. createTableSQL += ","
  145. }
  146. if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 {
  147. createTableSQL += "PRIMARY KEY ?,"
  148. primaryKeys := []interface{}{}
  149. for _, field := range stmt.Schema.PrimaryFields {
  150. primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
  151. }
  152. values = append(values, primaryKeys)
  153. }
  154. for _, idx := range stmt.Schema.ParseIndexes() {
  155. if m.CreateIndexAfterCreateTable {
  156. defer func(value interface{}, name string) {
  157. if errr == nil {
  158. errr = tx.Migrator().CreateIndex(value, name)
  159. }
  160. }(value, idx.Name)
  161. } else {
  162. if idx.Class != "" {
  163. createTableSQL += idx.Class + " "
  164. }
  165. createTableSQL += "INDEX ? ?"
  166. if idx.Option != "" {
  167. createTableSQL += " " + idx.Option
  168. }
  169. createTableSQL += ","
  170. values = append(values, clause.Expr{SQL: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
  171. }
  172. }
  173. for _, rel := range stmt.Schema.Relationships.Relations {
  174. if !m.DB.DisableForeignKeyConstraintWhenMigrating {
  175. if constraint := rel.ParseConstraint(); constraint != nil {
  176. if constraint.Schema == stmt.Schema {
  177. sql, vars := buildConstraint(constraint)
  178. createTableSQL += sql + ","
  179. values = append(values, vars...)
  180. }
  181. }
  182. }
  183. }
  184. for _, chk := range stmt.Schema.ParseCheckConstraints() {
  185. createTableSQL += "CONSTRAINT ? CHECK (?),"
  186. values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
  187. }
  188. createTableSQL = strings.TrimSuffix(createTableSQL, ",")
  189. createTableSQL += ")"
  190. if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
  191. createTableSQL += fmt.Sprint(tableOption)
  192. }
  193. errr = tx.Exec(createTableSQL, values...).Error
  194. return errr
  195. }); err != nil {
  196. return err
  197. }
  198. }
  199. return nil
  200. }
  201. func (m Migrator) DropTable(values ...interface{}) error {
  202. values = m.ReorderModels(values, false)
  203. for i := len(values) - 1; i >= 0; i-- {
  204. tx := m.DB.Session(&gorm.Session{})
  205. if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
  206. return tx.Exec("DROP TABLE IF EXISTS ?", m.CurrentTable(stmt)).Error
  207. }); err != nil {
  208. return err
  209. }
  210. }
  211. return nil
  212. }
  213. func (m Migrator) HasTable(value interface{}) bool {
  214. var count int64
  215. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  216. currentDatabase := m.DB.Migrator().CurrentDatabase()
  217. return m.DB.Raw("SELECT count(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ? AND table_type = ?", currentDatabase, stmt.Table, "BASE TABLE").Row().Scan(&count)
  218. })
  219. return count > 0
  220. }
  221. func (m Migrator) RenameTable(oldName, newName interface{}) error {
  222. var oldTable, newTable interface{}
  223. if v, ok := oldName.(string); ok {
  224. oldTable = clause.Table{Name: v}
  225. } else {
  226. stmt := &gorm.Statement{DB: m.DB}
  227. if err := stmt.Parse(oldName); err == nil {
  228. oldTable = m.CurrentTable(stmt)
  229. } else {
  230. return err
  231. }
  232. }
  233. if v, ok := newName.(string); ok {
  234. newTable = clause.Table{Name: v}
  235. } else {
  236. stmt := &gorm.Statement{DB: m.DB}
  237. if err := stmt.Parse(newName); err == nil {
  238. newTable = m.CurrentTable(stmt)
  239. } else {
  240. return err
  241. }
  242. }
  243. return m.DB.Exec("ALTER TABLE ? RENAME TO ?", oldTable, newTable).Error
  244. }
  245. func (m Migrator) AddColumn(value interface{}, field string) error {
  246. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  247. if field := stmt.Schema.LookUpField(field); field != nil {
  248. return m.DB.Exec(
  249. "ALTER TABLE ? ADD ? ?",
  250. m.CurrentTable(stmt), clause.Column{Name: field.DBName}, m.DB.Migrator().FullDataTypeOf(field),
  251. ).Error
  252. }
  253. return fmt.Errorf("failed to look up field with name: %s", field)
  254. })
  255. }
  256. func (m Migrator) DropColumn(value interface{}, name string) error {
  257. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  258. if field := stmt.Schema.LookUpField(name); field != nil {
  259. name = field.DBName
  260. }
  261. return m.DB.Exec(
  262. "ALTER TABLE ? DROP COLUMN ?", m.CurrentTable(stmt), clause.Column{Name: name},
  263. ).Error
  264. })
  265. }
  266. func (m Migrator) AlterColumn(value interface{}, field string) error {
  267. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  268. if field := stmt.Schema.LookUpField(field); field != nil {
  269. fileType := clause.Expr{SQL: m.DataTypeOf(field)}
  270. return m.DB.Exec(
  271. "ALTER TABLE ? ALTER COLUMN ? TYPE ?",
  272. m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
  273. ).Error
  274. }
  275. return fmt.Errorf("failed to look up field with name: %s", field)
  276. })
  277. }
  278. func (m Migrator) HasColumn(value interface{}, field string) bool {
  279. var count int64
  280. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  281. currentDatabase := m.DB.Migrator().CurrentDatabase()
  282. name := field
  283. if field := stmt.Schema.LookUpField(field); field != nil {
  284. name = field.DBName
  285. }
  286. return m.DB.Raw(
  287. "SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?",
  288. currentDatabase, stmt.Table, name,
  289. ).Row().Scan(&count)
  290. })
  291. return count > 0
  292. }
  293. func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
  294. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  295. if field := stmt.Schema.LookUpField(oldName); field != nil {
  296. oldName = field.DBName
  297. }
  298. if field := stmt.Schema.LookUpField(newName); field != nil {
  299. newName = field.DBName
  300. }
  301. return m.DB.Exec(
  302. "ALTER TABLE ? RENAME COLUMN ? TO ?",
  303. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  304. ).Error
  305. })
  306. }
  307. func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
  308. // found, smart migrate
  309. fullDataType := strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL)
  310. realDataType := strings.ToLower(columnType.DatabaseTypeName())
  311. alterColumn := false
  312. // check size
  313. if length, _ := columnType.Length(); length != int64(field.Size) {
  314. if length > 0 && field.Size > 0 {
  315. alterColumn = true
  316. } else {
  317. // has size in data type and not equal
  318. matches := regexp.MustCompile(`[^\d](\d+)[^\d]?`).FindAllStringSubmatch(realDataType, -1)
  319. matches2 := regexp.MustCompile(`[^\d]*(\d+)[^\d]?`).FindAllStringSubmatch(fullDataType, -1)
  320. if (len(matches) == 1 && matches[0][1] != fmt.Sprint(field.Size) || !field.PrimaryKey) && (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length)) {
  321. alterColumn = true
  322. }
  323. }
  324. }
  325. // check precision
  326. if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
  327. if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
  328. alterColumn = true
  329. }
  330. }
  331. // check nullable
  332. if nullable, ok := columnType.Nullable(); ok && nullable == field.NotNull {
  333. // not primary key & database is nullable
  334. if !field.PrimaryKey && nullable {
  335. alterColumn = true
  336. }
  337. }
  338. if alterColumn {
  339. return m.DB.Migrator().AlterColumn(value, field.Name)
  340. }
  341. return nil
  342. }
  343. func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType, err error) {
  344. columnTypes = make([]gorm.ColumnType, 0)
  345. err = m.RunWithValue(value, func(stmt *gorm.Statement) error {
  346. rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
  347. if err == nil {
  348. defer rows.Close()
  349. rawColumnTypes, err := rows.ColumnTypes()
  350. if err == nil {
  351. for _, c := range rawColumnTypes {
  352. columnTypes = append(columnTypes, c)
  353. }
  354. }
  355. }
  356. return err
  357. })
  358. return
  359. }
  360. func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
  361. return gorm.ErrNotImplemented
  362. }
  363. func (m Migrator) DropView(name string) error {
  364. return gorm.ErrNotImplemented
  365. }
  366. func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
  367. sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
  368. if constraint.OnDelete != "" {
  369. sql += " ON DELETE " + constraint.OnDelete
  370. }
  371. if constraint.OnUpdate != "" {
  372. sql += " ON UPDATE " + constraint.OnUpdate
  373. }
  374. var foreignKeys, references []interface{}
  375. for _, field := range constraint.ForeignKeys {
  376. foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
  377. }
  378. for _, field := range constraint.References {
  379. references = append(references, clause.Column{Name: field.DBName})
  380. }
  381. results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
  382. return
  383. }
  384. func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_ *schema.Constraint, _ *schema.Check, table string) {
  385. if stmt.Schema == nil {
  386. return nil, nil, stmt.Table
  387. }
  388. checkConstraints := stmt.Schema.ParseCheckConstraints()
  389. if chk, ok := checkConstraints[name]; ok {
  390. return nil, &chk, stmt.Table
  391. }
  392. getTable := func(rel *schema.Relationship) string {
  393. switch rel.Type {
  394. case schema.HasOne, schema.HasMany:
  395. return rel.FieldSchema.Table
  396. case schema.Many2Many:
  397. return rel.JoinTable.Table
  398. }
  399. return stmt.Table
  400. }
  401. for _, rel := range stmt.Schema.Relationships.Relations {
  402. if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
  403. return constraint, nil, getTable(rel)
  404. }
  405. }
  406. if field := stmt.Schema.LookUpField(name); field != nil {
  407. for _, cc := range checkConstraints {
  408. if cc.Field == field {
  409. return nil, &cc, stmt.Table
  410. }
  411. }
  412. for _, rel := range stmt.Schema.Relationships.Relations {
  413. if constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {
  414. return constraint, nil, getTable(rel)
  415. }
  416. }
  417. }
  418. return nil, nil, ""
  419. }
  420. func (m Migrator) CreateConstraint(value interface{}, name string) error {
  421. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  422. constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
  423. if chk != nil {
  424. return m.DB.Exec(
  425. "ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
  426. m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
  427. ).Error
  428. }
  429. if constraint != nil {
  430. var vars = []interface{}{clause.Table{Name: table}}
  431. if stmt.TableExpr != nil {
  432. vars[0] = stmt.TableExpr
  433. }
  434. sql, values := buildConstraint(constraint)
  435. return m.DB.Exec("ALTER TABLE ? ADD "+sql, append(vars, values...)...).Error
  436. }
  437. return nil
  438. })
  439. }
  440. func (m Migrator) DropConstraint(value interface{}, name string) error {
  441. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  442. constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
  443. if constraint != nil {
  444. name = constraint.Name
  445. } else if chk != nil {
  446. name = chk.Name
  447. }
  448. return m.DB.Exec("ALTER TABLE ? DROP CONSTRAINT ?", clause.Table{Name: table}, clause.Column{Name: name}).Error
  449. })
  450. }
  451. func (m Migrator) HasConstraint(value interface{}, name string) bool {
  452. var count int64
  453. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  454. currentDatabase := m.DB.Migrator().CurrentDatabase()
  455. constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
  456. if constraint != nil {
  457. name = constraint.Name
  458. } else if chk != nil {
  459. name = chk.Name
  460. }
  461. return m.DB.Raw(
  462. "SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
  463. currentDatabase, table, name,
  464. ).Row().Scan(&count)
  465. })
  466. return count > 0
  467. }
  468. func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
  469. for _, opt := range opts {
  470. str := stmt.Quote(opt.DBName)
  471. if opt.Expression != "" {
  472. str = opt.Expression
  473. } else if opt.Length > 0 {
  474. str += fmt.Sprintf("(%d)", opt.Length)
  475. }
  476. if opt.Collate != "" {
  477. str += " COLLATE " + opt.Collate
  478. }
  479. if opt.Sort != "" {
  480. str += " " + opt.Sort
  481. }
  482. results = append(results, clause.Expr{SQL: str})
  483. }
  484. return
  485. }
  486. type BuildIndexOptionsInterface interface {
  487. BuildIndexOptions([]schema.IndexOption, *gorm.Statement) []interface{}
  488. }
  489. func (m Migrator) CreateIndex(value interface{}, name string) error {
  490. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  491. if idx := stmt.Schema.LookIndex(name); idx != nil {
  492. opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)
  493. values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
  494. createIndexSQL := "CREATE "
  495. if idx.Class != "" {
  496. createIndexSQL += idx.Class + " "
  497. }
  498. createIndexSQL += "INDEX ? ON ??"
  499. if idx.Type != "" {
  500. createIndexSQL += " USING " + idx.Type
  501. }
  502. if idx.Option != "" {
  503. createIndexSQL += " " + idx.Option
  504. }
  505. return m.DB.Exec(createIndexSQL, values...).Error
  506. }
  507. return fmt.Errorf("failed to create index with name %v", name)
  508. })
  509. }
  510. func (m Migrator) DropIndex(value interface{}, name string) error {
  511. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  512. if idx := stmt.Schema.LookIndex(name); idx != nil {
  513. name = idx.Name
  514. }
  515. return m.DB.Exec("DROP INDEX ? ON ?", clause.Column{Name: name}, m.CurrentTable(stmt)).Error
  516. })
  517. }
  518. func (m Migrator) HasIndex(value interface{}, name string) bool {
  519. var count int64
  520. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  521. currentDatabase := m.DB.Migrator().CurrentDatabase()
  522. if idx := stmt.Schema.LookIndex(name); idx != nil {
  523. name = idx.Name
  524. }
  525. return m.DB.Raw(
  526. "SELECT count(*) FROM information_schema.statistics WHERE table_schema = ? AND table_name = ? AND index_name = ?",
  527. currentDatabase, stmt.Table, name,
  528. ).Row().Scan(&count)
  529. })
  530. return count > 0
  531. }
  532. func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
  533. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  534. return m.DB.Exec(
  535. "ALTER TABLE ? RENAME INDEX ? TO ?",
  536. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  537. ).Error
  538. })
  539. }
  540. func (m Migrator) CurrentDatabase() (name string) {
  541. m.DB.Raw("SELECT DATABASE()").Row().Scan(&name)
  542. return
  543. }
  544. // ReorderModels reorder models according to constraint dependencies
  545. func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []interface{}) {
  546. type Dependency struct {
  547. *gorm.Statement
  548. Depends []*schema.Schema
  549. }
  550. var (
  551. modelNames, orderedModelNames []string
  552. orderedModelNamesMap = map[string]bool{}
  553. parsedSchemas = map[*schema.Schema]bool{}
  554. valuesMap = map[string]Dependency{}
  555. insertIntoOrderedList func(name string)
  556. parseDependence func(value interface{}, addToList bool)
  557. )
  558. parseDependence = func(value interface{}, addToList bool) {
  559. dep := Dependency{
  560. Statement: &gorm.Statement{DB: m.DB, Dest: value},
  561. }
  562. beDependedOn := map[*schema.Schema]bool{}
  563. if err := dep.Parse(value); err != nil {
  564. m.DB.Logger.Error(context.Background(), "failed to parse value %#v, got error %v", value, err)
  565. }
  566. if _, ok := parsedSchemas[dep.Statement.Schema]; ok {
  567. return
  568. }
  569. parsedSchemas[dep.Statement.Schema] = true
  570. for _, rel := range dep.Schema.Relationships.Relations {
  571. if c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema {
  572. dep.Depends = append(dep.Depends, c.ReferenceSchema)
  573. }
  574. if rel.Type == schema.HasOne || rel.Type == schema.HasMany {
  575. beDependedOn[rel.FieldSchema] = true
  576. }
  577. if rel.JoinTable != nil {
  578. // append join value
  579. defer func(rel *schema.Relationship, joinValue interface{}) {
  580. if !beDependedOn[rel.FieldSchema] {
  581. dep.Depends = append(dep.Depends, rel.FieldSchema)
  582. } else {
  583. fieldValue := reflect.New(rel.FieldSchema.ModelType).Interface()
  584. parseDependence(fieldValue, autoAdd)
  585. }
  586. parseDependence(joinValue, autoAdd)
  587. }(rel, reflect.New(rel.JoinTable.ModelType).Interface())
  588. }
  589. }
  590. valuesMap[dep.Schema.Table] = dep
  591. if addToList {
  592. modelNames = append(modelNames, dep.Schema.Table)
  593. }
  594. }
  595. insertIntoOrderedList = func(name string) {
  596. if _, ok := orderedModelNamesMap[name]; ok {
  597. return // avoid loop
  598. }
  599. orderedModelNamesMap[name] = true
  600. if autoAdd {
  601. dep := valuesMap[name]
  602. for _, d := range dep.Depends {
  603. if _, ok := valuesMap[d.Table]; ok {
  604. insertIntoOrderedList(d.Table)
  605. } else {
  606. parseDependence(reflect.New(d.ModelType).Interface(), autoAdd)
  607. insertIntoOrderedList(d.Table)
  608. }
  609. }
  610. }
  611. orderedModelNames = append(orderedModelNames, name)
  612. }
  613. for _, value := range values {
  614. if v, ok := value.(string); ok {
  615. results = append(results, v)
  616. } else {
  617. parseDependence(value, true)
  618. }
  619. }
  620. for _, name := range modelNames {
  621. insertIntoOrderedList(name)
  622. }
  623. for _, name := range orderedModelNames {
  624. results = append(results, valuesMap[name].Statement.Dest)
  625. }
  626. return
  627. }
  628. func (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {
  629. if stmt.TableExpr != nil {
  630. return *stmt.TableExpr
  631. }
  632. return clause.Table{Name: stmt.Table}
  633. }