migrator.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  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. errr = tx.Migrator().CreateIndex(value, name)
  158. }(value, idx.Name)
  159. } else {
  160. if idx.Class != "" {
  161. createTableSQL += idx.Class + " "
  162. }
  163. createTableSQL += "INDEX ? ?"
  164. if idx.Option != "" {
  165. createTableSQL += " " + idx.Option
  166. }
  167. createTableSQL += ","
  168. values = append(values, clause.Expr{SQL: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
  169. }
  170. }
  171. for _, rel := range stmt.Schema.Relationships.Relations {
  172. if !m.DB.DisableForeignKeyConstraintWhenMigrating {
  173. if constraint := rel.ParseConstraint(); constraint != nil {
  174. if constraint.Schema == stmt.Schema {
  175. sql, vars := buildConstraint(constraint)
  176. createTableSQL += sql + ","
  177. values = append(values, vars...)
  178. }
  179. }
  180. }
  181. }
  182. for _, chk := range stmt.Schema.ParseCheckConstraints() {
  183. createTableSQL += "CONSTRAINT ? CHECK (?),"
  184. values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
  185. }
  186. createTableSQL = strings.TrimSuffix(createTableSQL, ",")
  187. createTableSQL += ")"
  188. if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
  189. createTableSQL += fmt.Sprint(tableOption)
  190. }
  191. errr = tx.Exec(createTableSQL, values...).Error
  192. return errr
  193. }); err != nil {
  194. return err
  195. }
  196. }
  197. return nil
  198. }
  199. func (m Migrator) DropTable(values ...interface{}) error {
  200. values = m.ReorderModels(values, false)
  201. for i := len(values) - 1; i >= 0; i-- {
  202. tx := m.DB.Session(&gorm.Session{})
  203. if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
  204. return tx.Exec("DROP TABLE IF EXISTS ?", m.CurrentTable(stmt)).Error
  205. }); err != nil {
  206. return err
  207. }
  208. }
  209. return nil
  210. }
  211. func (m Migrator) HasTable(value interface{}) bool {
  212. var count int64
  213. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  214. currentDatabase := m.DB.Migrator().CurrentDatabase()
  215. 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)
  216. })
  217. return count > 0
  218. }
  219. func (m Migrator) RenameTable(oldName, newName interface{}) error {
  220. var oldTable, newTable interface{}
  221. if v, ok := oldName.(string); ok {
  222. oldTable = clause.Table{Name: v}
  223. } else {
  224. stmt := &gorm.Statement{DB: m.DB}
  225. if err := stmt.Parse(oldName); err == nil {
  226. oldTable = m.CurrentTable(stmt)
  227. } else {
  228. return err
  229. }
  230. }
  231. if v, ok := newName.(string); ok {
  232. newTable = clause.Table{Name: v}
  233. } else {
  234. stmt := &gorm.Statement{DB: m.DB}
  235. if err := stmt.Parse(newName); err == nil {
  236. newTable = m.CurrentTable(stmt)
  237. } else {
  238. return err
  239. }
  240. }
  241. return m.DB.Exec("ALTER TABLE ? RENAME TO ?", oldTable, newTable).Error
  242. }
  243. func (m Migrator) AddColumn(value interface{}, field string) error {
  244. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  245. if field := stmt.Schema.LookUpField(field); field != nil {
  246. return m.DB.Exec(
  247. "ALTER TABLE ? ADD ? ?",
  248. m.CurrentTable(stmt), clause.Column{Name: field.DBName}, m.DB.Migrator().FullDataTypeOf(field),
  249. ).Error
  250. }
  251. return fmt.Errorf("failed to look up field with name: %s", field)
  252. })
  253. }
  254. func (m Migrator) DropColumn(value interface{}, name string) error {
  255. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  256. if field := stmt.Schema.LookUpField(name); field != nil {
  257. name = field.DBName
  258. }
  259. return m.DB.Exec(
  260. "ALTER TABLE ? DROP COLUMN ?", m.CurrentTable(stmt), clause.Column{Name: name},
  261. ).Error
  262. })
  263. }
  264. func (m Migrator) AlterColumn(value interface{}, field string) error {
  265. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  266. if field := stmt.Schema.LookUpField(field); field != nil {
  267. fileType := clause.Expr{SQL: m.DataTypeOf(field)}
  268. return m.DB.Exec(
  269. "ALTER TABLE ? ALTER COLUMN ? TYPE ?",
  270. m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
  271. ).Error
  272. }
  273. return fmt.Errorf("failed to look up field with name: %s", field)
  274. })
  275. }
  276. func (m Migrator) HasColumn(value interface{}, field string) bool {
  277. var count int64
  278. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  279. currentDatabase := m.DB.Migrator().CurrentDatabase()
  280. name := field
  281. if field := stmt.Schema.LookUpField(field); field != nil {
  282. name = field.DBName
  283. }
  284. return m.DB.Raw(
  285. "SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?",
  286. currentDatabase, stmt.Table, name,
  287. ).Row().Scan(&count)
  288. })
  289. return count > 0
  290. }
  291. func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
  292. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  293. if field := stmt.Schema.LookUpField(oldName); field != nil {
  294. oldName = field.DBName
  295. }
  296. if field := stmt.Schema.LookUpField(newName); field != nil {
  297. newName = field.DBName
  298. }
  299. return m.DB.Exec(
  300. "ALTER TABLE ? RENAME COLUMN ? TO ?",
  301. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  302. ).Error
  303. })
  304. }
  305. func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
  306. // found, smart migrate
  307. fullDataType := strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL)
  308. realDataType := strings.ToLower(columnType.DatabaseTypeName())
  309. alterColumn := false
  310. // check size
  311. if length, _ := columnType.Length(); length != int64(field.Size) {
  312. if length > 0 && field.Size > 0 {
  313. alterColumn = true
  314. } else {
  315. // has size in data type and not equal
  316. matches := regexp.MustCompile(`[^\d](\d+)[^\d]?`).FindAllStringSubmatch(realDataType, -1)
  317. matches2 := regexp.MustCompile(`[^\d]*(\d+)[^\d]?`).FindAllStringSubmatch(fullDataType, -1)
  318. if (len(matches) == 1 && matches[0][1] != fmt.Sprint(field.Size) || !field.PrimaryKey) && (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length)) {
  319. alterColumn = true
  320. }
  321. }
  322. }
  323. // check precision
  324. if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
  325. if strings.Contains(fullDataType, fmt.Sprint(field.Precision)) {
  326. alterColumn = true
  327. }
  328. }
  329. // check nullable
  330. if nullable, ok := columnType.Nullable(); ok && nullable == field.NotNull {
  331. // not primary key & database is nullable
  332. if !field.PrimaryKey && nullable {
  333. alterColumn = true
  334. }
  335. }
  336. if alterColumn {
  337. return m.DB.Migrator().AlterColumn(value, field.Name)
  338. }
  339. return nil
  340. }
  341. func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType, err error) {
  342. columnTypes = make([]gorm.ColumnType, 0)
  343. err = m.RunWithValue(value, func(stmt *gorm.Statement) error {
  344. rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
  345. if err == nil {
  346. defer rows.Close()
  347. rawColumnTypes, err := rows.ColumnTypes()
  348. if err == nil {
  349. for _, c := range rawColumnTypes {
  350. columnTypes = append(columnTypes, c)
  351. }
  352. }
  353. }
  354. return err
  355. })
  356. return
  357. }
  358. func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
  359. return gorm.ErrNotImplemented
  360. }
  361. func (m Migrator) DropView(name string) error {
  362. return gorm.ErrNotImplemented
  363. }
  364. func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
  365. sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
  366. if constraint.OnDelete != "" {
  367. sql += " ON DELETE " + constraint.OnDelete
  368. }
  369. if constraint.OnUpdate != "" {
  370. sql += " ON UPDATE " + constraint.OnUpdate
  371. }
  372. var foreignKeys, references []interface{}
  373. for _, field := range constraint.ForeignKeys {
  374. foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
  375. }
  376. for _, field := range constraint.References {
  377. references = append(references, clause.Column{Name: field.DBName})
  378. }
  379. results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
  380. return
  381. }
  382. func (m Migrator) CreateConstraint(value interface{}, name string) error {
  383. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  384. checkConstraints := stmt.Schema.ParseCheckConstraints()
  385. if chk, ok := checkConstraints[name]; ok {
  386. return m.DB.Exec(
  387. "ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
  388. m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
  389. ).Error
  390. }
  391. for _, rel := range stmt.Schema.Relationships.Relations {
  392. if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
  393. sql, values := buildConstraint(constraint)
  394. return m.DB.Exec("ALTER TABLE ? ADD "+sql, append([]interface{}{m.CurrentTable(stmt)}, values...)...).Error
  395. }
  396. }
  397. err := fmt.Errorf("failed to create constraint with name %v", name)
  398. if field := stmt.Schema.LookUpField(name); field != nil {
  399. for _, cc := range checkConstraints {
  400. if err = m.DB.Migrator().CreateIndex(value, cc.Name); err != nil {
  401. return err
  402. }
  403. }
  404. for _, rel := range stmt.Schema.Relationships.Relations {
  405. if constraint := rel.ParseConstraint(); constraint != nil && constraint.Field == field {
  406. if err = m.DB.Migrator().CreateIndex(value, constraint.Name); err != nil {
  407. return err
  408. }
  409. }
  410. }
  411. }
  412. return err
  413. })
  414. }
  415. func (m Migrator) DropConstraint(value interface{}, name string) error {
  416. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  417. return m.DB.Exec(
  418. "ALTER TABLE ? DROP CONSTRAINT ?",
  419. m.CurrentTable(stmt), clause.Column{Name: name},
  420. ).Error
  421. })
  422. }
  423. func (m Migrator) HasConstraint(value interface{}, name string) bool {
  424. var count int64
  425. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  426. currentDatabase := m.DB.Migrator().CurrentDatabase()
  427. return m.DB.Raw(
  428. "SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
  429. currentDatabase, stmt.Table, name,
  430. ).Row().Scan(&count)
  431. })
  432. return count > 0
  433. }
  434. func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
  435. for _, opt := range opts {
  436. str := stmt.Quote(opt.DBName)
  437. if opt.Expression != "" {
  438. str = opt.Expression
  439. } else if opt.Length > 0 {
  440. str += fmt.Sprintf("(%d)", opt.Length)
  441. }
  442. if opt.Collate != "" {
  443. str += " COLLATE " + opt.Collate
  444. }
  445. if opt.Sort != "" {
  446. str += " " + opt.Sort
  447. }
  448. results = append(results, clause.Expr{SQL: str})
  449. }
  450. return
  451. }
  452. type BuildIndexOptionsInterface interface {
  453. BuildIndexOptions([]schema.IndexOption, *gorm.Statement) []interface{}
  454. }
  455. func (m Migrator) CreateIndex(value interface{}, name string) error {
  456. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  457. if idx := stmt.Schema.LookIndex(name); idx != nil {
  458. opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)
  459. values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
  460. createIndexSQL := "CREATE "
  461. if idx.Class != "" {
  462. createIndexSQL += idx.Class + " "
  463. }
  464. createIndexSQL += "INDEX ? ON ??"
  465. if idx.Type != "" {
  466. createIndexSQL += " USING " + idx.Type
  467. }
  468. if idx.Option != "" {
  469. createIndexSQL += " " + idx.Option
  470. }
  471. return m.DB.Exec(createIndexSQL, values...).Error
  472. }
  473. return fmt.Errorf("failed to create index with name %v", name)
  474. })
  475. }
  476. func (m Migrator) DropIndex(value interface{}, name string) error {
  477. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  478. if idx := stmt.Schema.LookIndex(name); idx != nil {
  479. name = idx.Name
  480. }
  481. return m.DB.Exec("DROP INDEX ? ON ?", clause.Column{Name: name}, m.CurrentTable(stmt)).Error
  482. })
  483. }
  484. func (m Migrator) HasIndex(value interface{}, name string) bool {
  485. var count int64
  486. m.RunWithValue(value, func(stmt *gorm.Statement) error {
  487. currentDatabase := m.DB.Migrator().CurrentDatabase()
  488. if idx := stmt.Schema.LookIndex(name); idx != nil {
  489. name = idx.Name
  490. }
  491. return m.DB.Raw(
  492. "SELECT count(*) FROM information_schema.statistics WHERE table_schema = ? AND table_name = ? AND index_name = ?",
  493. currentDatabase, stmt.Table, name,
  494. ).Row().Scan(&count)
  495. })
  496. return count > 0
  497. }
  498. func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
  499. return m.RunWithValue(value, func(stmt *gorm.Statement) error {
  500. return m.DB.Exec(
  501. "ALTER TABLE ? RENAME INDEX ? TO ?",
  502. m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
  503. ).Error
  504. })
  505. }
  506. func (m Migrator) CurrentDatabase() (name string) {
  507. m.DB.Raw("SELECT DATABASE()").Row().Scan(&name)
  508. return
  509. }
  510. // ReorderModels reorder models according to constraint dependencies
  511. func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []interface{}) {
  512. type Dependency struct {
  513. *gorm.Statement
  514. Depends []*schema.Schema
  515. }
  516. var (
  517. modelNames, orderedModelNames []string
  518. orderedModelNamesMap = map[string]bool{}
  519. parsedSchemas = map[*schema.Schema]bool{}
  520. valuesMap = map[string]Dependency{}
  521. insertIntoOrderedList func(name string)
  522. parseDependence func(value interface{}, addToList bool)
  523. )
  524. parseDependence = func(value interface{}, addToList bool) {
  525. dep := Dependency{
  526. Statement: &gorm.Statement{DB: m.DB, Dest: value},
  527. }
  528. beDependedOn := map[*schema.Schema]bool{}
  529. if err := dep.Parse(value); err != nil {
  530. m.DB.Logger.Error(context.Background(), "failed to parse value %#v, got error %v", value, err)
  531. }
  532. if _, ok := parsedSchemas[dep.Statement.Schema]; ok {
  533. return
  534. }
  535. parsedSchemas[dep.Statement.Schema] = true
  536. for _, rel := range dep.Schema.Relationships.Relations {
  537. if c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema {
  538. dep.Depends = append(dep.Depends, c.ReferenceSchema)
  539. }
  540. if rel.Type == schema.HasOne || rel.Type == schema.HasMany {
  541. beDependedOn[rel.FieldSchema] = true
  542. }
  543. if rel.JoinTable != nil {
  544. // append join value
  545. defer func(rel *schema.Relationship, joinValue interface{}) {
  546. if !beDependedOn[rel.FieldSchema] {
  547. dep.Depends = append(dep.Depends, rel.FieldSchema)
  548. } else {
  549. fieldValue := reflect.New(rel.FieldSchema.ModelType).Interface()
  550. parseDependence(fieldValue, autoAdd)
  551. }
  552. parseDependence(joinValue, autoAdd)
  553. }(rel, reflect.New(rel.JoinTable.ModelType).Interface())
  554. }
  555. }
  556. valuesMap[dep.Schema.Table] = dep
  557. if addToList {
  558. modelNames = append(modelNames, dep.Schema.Table)
  559. }
  560. }
  561. insertIntoOrderedList = func(name string) {
  562. if _, ok := orderedModelNamesMap[name]; ok {
  563. return // avoid loop
  564. }
  565. orderedModelNamesMap[name] = true
  566. dep := valuesMap[name]
  567. for _, d := range dep.Depends {
  568. if _, ok := valuesMap[d.Table]; ok {
  569. insertIntoOrderedList(d.Table)
  570. } else if autoAdd {
  571. parseDependence(reflect.New(d.ModelType).Interface(), autoAdd)
  572. insertIntoOrderedList(d.Table)
  573. }
  574. }
  575. orderedModelNames = append(orderedModelNames, name)
  576. }
  577. for _, value := range values {
  578. if v, ok := value.(string); ok {
  579. results = append(results, v)
  580. } else {
  581. parseDependence(value, true)
  582. }
  583. }
  584. for _, name := range modelNames {
  585. insertIntoOrderedList(name)
  586. }
  587. for _, name := range orderedModelNames {
  588. results = append(results, valuesMap[name].Statement.Dest)
  589. }
  590. return
  591. }
  592. func (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {
  593. if stmt.TableExpr != nil {
  594. return *stmt.TableExpr
  595. }
  596. return clause.Table{Name: stmt.Table}
  597. }