123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 |
- package migrator
- import (
- "context"
- "fmt"
- "reflect"
- "regexp"
- "strings"
- "gorm.io/gorm"
- "gorm.io/gorm/clause"
- "gorm.io/gorm/schema"
- )
- // Migrator m struct
- type Migrator struct {
- Config
- }
- // Config schema config
- type Config struct {
- CreateIndexAfterCreateTable bool
- DB *gorm.DB
- gorm.Dialector
- }
- type GormDataTypeInterface interface {
- GormDBDataType(*gorm.DB, *schema.Field) string
- }
- func (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statement) error) error {
- stmt := &gorm.Statement{DB: m.DB}
- if m.DB.Statement != nil {
- stmt.Table = m.DB.Statement.Table
- stmt.TableExpr = m.DB.Statement.TableExpr
- }
- if table, ok := value.(string); ok {
- stmt.Table = table
- } else if err := stmt.Parse(value); err != nil {
- return err
- }
- return fc(stmt)
- }
- func (m Migrator) DataTypeOf(field *schema.Field) string {
- fieldValue := reflect.New(field.IndirectFieldType)
- if dataTyper, ok := fieldValue.Interface().(GormDataTypeInterface); ok {
- if dataType := dataTyper.GormDBDataType(m.DB, field); dataType != "" {
- return dataType
- }
- }
- return m.Dialector.DataTypeOf(field)
- }
- func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
- expr.SQL = m.DataTypeOf(field)
- if field.NotNull {
- expr.SQL += " NOT NULL"
- }
- if field.Unique {
- expr.SQL += " UNIQUE"
- }
- if field.HasDefaultValue && (field.DefaultValueInterface != nil || field.DefaultValue != "") {
- if field.DefaultValueInterface != nil {
- defaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValueInterface}}
- m.Dialector.BindVarTo(defaultStmt, defaultStmt, field.DefaultValueInterface)
- expr.SQL += " DEFAULT " + m.Dialector.Explain(defaultStmt.SQL.String(), field.DefaultValueInterface)
- } else if field.DefaultValue != "(-)" {
- expr.SQL += " DEFAULT " + field.DefaultValue
- }
- }
- return
- }
- // AutoMigrate
- func (m Migrator) AutoMigrate(values ...interface{}) error {
- for _, value := range m.ReorderModels(values, true) {
- tx := m.DB.Session(&gorm.Session{})
- if !tx.Migrator().HasTable(value) {
- if err := tx.Migrator().CreateTable(value); err != nil {
- return err
- }
- } else {
- if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
- columnTypes, _ := m.DB.Migrator().ColumnTypes(value)
- for _, field := range stmt.Schema.FieldsByDBName {
- var foundColumn gorm.ColumnType
- for _, columnType := range columnTypes {
- if columnType.Name() == field.DBName {
- foundColumn = columnType
- break
- }
- }
- if foundColumn == nil {
- // not found, add column
- if err := tx.Migrator().AddColumn(value, field.DBName); err != nil {
- return err
- }
- } else if err := m.DB.Migrator().MigrateColumn(value, field, foundColumn); err != nil {
- // found, smart migrate
- return err
- }
- }
- for _, rel := range stmt.Schema.Relationships.Relations {
- if !m.DB.Config.DisableForeignKeyConstraintWhenMigrating {
- if constraint := rel.ParseConstraint(); constraint != nil {
- if constraint.Schema == stmt.Schema {
- if !tx.Migrator().HasConstraint(value, constraint.Name) {
- if err := tx.Migrator().CreateConstraint(value, constraint.Name); err != nil {
- return err
- }
- }
- }
- }
- }
- for _, chk := range stmt.Schema.ParseCheckConstraints() {
- if !tx.Migrator().HasConstraint(value, chk.Name) {
- if err := tx.Migrator().CreateConstraint(value, chk.Name); err != nil {
- return err
- }
- }
- }
- }
- for _, idx := range stmt.Schema.ParseIndexes() {
- if !tx.Migrator().HasIndex(value, idx.Name) {
- if err := tx.Migrator().CreateIndex(value, idx.Name); err != nil {
- return err
- }
- }
- }
- return nil
- }); err != nil {
- return err
- }
- }
- }
- return nil
- }
- func (m Migrator) CreateTable(values ...interface{}) error {
- for _, value := range m.ReorderModels(values, false) {
- tx := m.DB.Session(&gorm.Session{})
- if err := m.RunWithValue(value, func(stmt *gorm.Statement) (errr error) {
- var (
- createTableSQL = "CREATE TABLE ? ("
- values = []interface{}{m.CurrentTable(stmt)}
- hasPrimaryKeyInDataType bool
- )
- for _, dbName := range stmt.Schema.DBNames {
- field := stmt.Schema.FieldsByDBName[dbName]
- createTableSQL += "? ?"
- hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(string(field.DataType)), "PRIMARY KEY")
- values = append(values, clause.Column{Name: dbName}, m.DB.Migrator().FullDataTypeOf(field))
- createTableSQL += ","
- }
- if !hasPrimaryKeyInDataType && len(stmt.Schema.PrimaryFields) > 0 {
- createTableSQL += "PRIMARY KEY ?,"
- primaryKeys := []interface{}{}
- for _, field := range stmt.Schema.PrimaryFields {
- primaryKeys = append(primaryKeys, clause.Column{Name: field.DBName})
- }
- values = append(values, primaryKeys)
- }
- for _, idx := range stmt.Schema.ParseIndexes() {
- if m.CreateIndexAfterCreateTable {
- defer func(value interface{}, name string) {
- if errr == nil {
- errr = tx.Migrator().CreateIndex(value, name)
- }
- }(value, idx.Name)
- } else {
- if idx.Class != "" {
- createTableSQL += idx.Class + " "
- }
- createTableSQL += "INDEX ? ?"
- if idx.Option != "" {
- createTableSQL += " " + idx.Option
- }
- createTableSQL += ","
- values = append(values, clause.Expr{SQL: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
- }
- }
- for _, rel := range stmt.Schema.Relationships.Relations {
- if !m.DB.DisableForeignKeyConstraintWhenMigrating {
- if constraint := rel.ParseConstraint(); constraint != nil {
- if constraint.Schema == stmt.Schema {
- sql, vars := buildConstraint(constraint)
- createTableSQL += sql + ","
- values = append(values, vars...)
- }
- }
- }
- }
- for _, chk := range stmt.Schema.ParseCheckConstraints() {
- createTableSQL += "CONSTRAINT ? CHECK (?),"
- values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
- }
- createTableSQL = strings.TrimSuffix(createTableSQL, ",")
- createTableSQL += ")"
- if tableOption, ok := m.DB.Get("gorm:table_options"); ok {
- createTableSQL += fmt.Sprint(tableOption)
- }
- errr = tx.Exec(createTableSQL, values...).Error
- return errr
- }); err != nil {
- return err
- }
- }
- return nil
- }
- func (m Migrator) DropTable(values ...interface{}) error {
- values = m.ReorderModels(values, false)
- for i := len(values) - 1; i >= 0; i-- {
- tx := m.DB.Session(&gorm.Session{})
- if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
- return tx.Exec("DROP TABLE IF EXISTS ?", m.CurrentTable(stmt)).Error
- }); err != nil {
- return err
- }
- }
- return nil
- }
- func (m Migrator) HasTable(value interface{}) bool {
- var count int64
- m.RunWithValue(value, func(stmt *gorm.Statement) error {
- currentDatabase := m.DB.Migrator().CurrentDatabase()
- 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)
- })
- return count > 0
- }
- func (m Migrator) RenameTable(oldName, newName interface{}) error {
- var oldTable, newTable interface{}
- if v, ok := oldName.(string); ok {
- oldTable = clause.Table{Name: v}
- } else {
- stmt := &gorm.Statement{DB: m.DB}
- if err := stmt.Parse(oldName); err == nil {
- oldTable = m.CurrentTable(stmt)
- } else {
- return err
- }
- }
- if v, ok := newName.(string); ok {
- newTable = clause.Table{Name: v}
- } else {
- stmt := &gorm.Statement{DB: m.DB}
- if err := stmt.Parse(newName); err == nil {
- newTable = m.CurrentTable(stmt)
- } else {
- return err
- }
- }
- return m.DB.Exec("ALTER TABLE ? RENAME TO ?", oldTable, newTable).Error
- }
- func (m Migrator) AddColumn(value interface{}, field string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if field := stmt.Schema.LookUpField(field); field != nil {
- return m.DB.Exec(
- "ALTER TABLE ? ADD ? ?",
- m.CurrentTable(stmt), clause.Column{Name: field.DBName}, m.DB.Migrator().FullDataTypeOf(field),
- ).Error
- }
- return fmt.Errorf("failed to look up field with name: %s", field)
- })
- }
- func (m Migrator) DropColumn(value interface{}, name string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if field := stmt.Schema.LookUpField(name); field != nil {
- name = field.DBName
- }
- return m.DB.Exec(
- "ALTER TABLE ? DROP COLUMN ?", m.CurrentTable(stmt), clause.Column{Name: name},
- ).Error
- })
- }
- func (m Migrator) AlterColumn(value interface{}, field string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if field := stmt.Schema.LookUpField(field); field != nil {
- fileType := clause.Expr{SQL: m.DataTypeOf(field)}
- return m.DB.Exec(
- "ALTER TABLE ? ALTER COLUMN ? TYPE ?",
- m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
- ).Error
- }
- return fmt.Errorf("failed to look up field with name: %s", field)
- })
- }
- func (m Migrator) HasColumn(value interface{}, field string) bool {
- var count int64
- m.RunWithValue(value, func(stmt *gorm.Statement) error {
- currentDatabase := m.DB.Migrator().CurrentDatabase()
- name := field
- if field := stmt.Schema.LookUpField(field); field != nil {
- name = field.DBName
- }
- return m.DB.Raw(
- "SELECT count(*) FROM INFORMATION_SCHEMA.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?",
- currentDatabase, stmt.Table, name,
- ).Row().Scan(&count)
- })
- return count > 0
- }
- func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if field := stmt.Schema.LookUpField(oldName); field != nil {
- oldName = field.DBName
- }
- if field := stmt.Schema.LookUpField(newName); field != nil {
- newName = field.DBName
- }
- return m.DB.Exec(
- "ALTER TABLE ? RENAME COLUMN ? TO ?",
- m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
- ).Error
- })
- }
- func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
- // found, smart migrate
- fullDataType := strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL)
- realDataType := strings.ToLower(columnType.DatabaseTypeName())
- alterColumn := false
- // check size
- if length, _ := columnType.Length(); length != int64(field.Size) {
- if length > 0 && field.Size > 0 {
- alterColumn = true
- } else {
- // has size in data type and not equal
- matches := regexp.MustCompile(`[^\d](\d+)[^\d]?`).FindAllStringSubmatch(realDataType, -1)
- matches2 := regexp.MustCompile(`[^\d]*(\d+)[^\d]?`).FindAllStringSubmatch(fullDataType, -1)
- if (len(matches) == 1 && matches[0][1] != fmt.Sprint(field.Size) || !field.PrimaryKey) && (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length)) {
- alterColumn = true
- }
- }
- }
- // check precision
- if precision, _, ok := columnType.DecimalSize(); ok && int64(field.Precision) != precision {
- if regexp.MustCompile(fmt.Sprintf("[^0-9]%d[^0-9]", field.Precision)).MatchString(m.DataTypeOf(field)) {
- alterColumn = true
- }
- }
- // check nullable
- if nullable, ok := columnType.Nullable(); ok && nullable == field.NotNull {
- // not primary key & database is nullable
- if !field.PrimaryKey && nullable {
- alterColumn = true
- }
- }
- if alterColumn {
- return m.DB.Migrator().AlterColumn(value, field.Name)
- }
- return nil
- }
- func (m Migrator) ColumnTypes(value interface{}) (columnTypes []gorm.ColumnType, err error) {
- columnTypes = make([]gorm.ColumnType, 0)
- err = m.RunWithValue(value, func(stmt *gorm.Statement) error {
- rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
- if err == nil {
- defer rows.Close()
- rawColumnTypes, err := rows.ColumnTypes()
- if err == nil {
- for _, c := range rawColumnTypes {
- columnTypes = append(columnTypes, c)
- }
- }
- }
- return err
- })
- return
- }
- func (m Migrator) CreateView(name string, option gorm.ViewOption) error {
- return gorm.ErrNotImplemented
- }
- func (m Migrator) DropView(name string) error {
- return gorm.ErrNotImplemented
- }
- func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
- sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
- if constraint.OnDelete != "" {
- sql += " ON DELETE " + constraint.OnDelete
- }
- if constraint.OnUpdate != "" {
- sql += " ON UPDATE " + constraint.OnUpdate
- }
- var foreignKeys, references []interface{}
- for _, field := range constraint.ForeignKeys {
- foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
- }
- for _, field := range constraint.References {
- references = append(references, clause.Column{Name: field.DBName})
- }
- results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
- return
- }
- func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_ *schema.Constraint, _ *schema.Check, table string) {
- if stmt.Schema == nil {
- return nil, nil, stmt.Table
- }
- checkConstraints := stmt.Schema.ParseCheckConstraints()
- if chk, ok := checkConstraints[name]; ok {
- return nil, &chk, stmt.Table
- }
- getTable := func(rel *schema.Relationship) string {
- switch rel.Type {
- case schema.HasOne, schema.HasMany:
- return rel.FieldSchema.Table
- case schema.Many2Many:
- return rel.JoinTable.Table
- }
- return stmt.Table
- }
- for _, rel := range stmt.Schema.Relationships.Relations {
- if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
- return constraint, nil, getTable(rel)
- }
- }
- if field := stmt.Schema.LookUpField(name); field != nil {
- for _, cc := range checkConstraints {
- if cc.Field == field {
- return nil, &cc, stmt.Table
- }
- }
- for _, rel := range stmt.Schema.Relationships.Relations {
- if constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {
- return constraint, nil, getTable(rel)
- }
- }
- }
- return nil, nil, ""
- }
- func (m Migrator) CreateConstraint(value interface{}, name string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
- if chk != nil {
- return m.DB.Exec(
- "ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
- m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
- ).Error
- }
- if constraint != nil {
- var vars = []interface{}{clause.Table{Name: table}}
- if stmt.TableExpr != nil {
- vars[0] = stmt.TableExpr
- }
- sql, values := buildConstraint(constraint)
- return m.DB.Exec("ALTER TABLE ? ADD "+sql, append(vars, values...)...).Error
- }
- return nil
- })
- }
- func (m Migrator) DropConstraint(value interface{}, name string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
- if constraint != nil {
- name = constraint.Name
- } else if chk != nil {
- name = chk.Name
- }
- return m.DB.Exec("ALTER TABLE ? DROP CONSTRAINT ?", clause.Table{Name: table}, clause.Column{Name: name}).Error
- })
- }
- func (m Migrator) HasConstraint(value interface{}, name string) bool {
- var count int64
- m.RunWithValue(value, func(stmt *gorm.Statement) error {
- currentDatabase := m.DB.Migrator().CurrentDatabase()
- constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
- if constraint != nil {
- name = constraint.Name
- } else if chk != nil {
- name = chk.Name
- }
- return m.DB.Raw(
- "SELECT count(*) FROM INFORMATION_SCHEMA.table_constraints WHERE constraint_schema = ? AND table_name = ? AND constraint_name = ?",
- currentDatabase, table, name,
- ).Row().Scan(&count)
- })
- return count > 0
- }
- func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) {
- for _, opt := range opts {
- str := stmt.Quote(opt.DBName)
- if opt.Expression != "" {
- str = opt.Expression
- } else if opt.Length > 0 {
- str += fmt.Sprintf("(%d)", opt.Length)
- }
- if opt.Collate != "" {
- str += " COLLATE " + opt.Collate
- }
- if opt.Sort != "" {
- str += " " + opt.Sort
- }
- results = append(results, clause.Expr{SQL: str})
- }
- return
- }
- type BuildIndexOptionsInterface interface {
- BuildIndexOptions([]schema.IndexOption, *gorm.Statement) []interface{}
- }
- func (m Migrator) CreateIndex(value interface{}, name string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if idx := stmt.Schema.LookIndex(name); idx != nil {
- opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)
- values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
- createIndexSQL := "CREATE "
- if idx.Class != "" {
- createIndexSQL += idx.Class + " "
- }
- createIndexSQL += "INDEX ? ON ??"
- if idx.Type != "" {
- createIndexSQL += " USING " + idx.Type
- }
- if idx.Option != "" {
- createIndexSQL += " " + idx.Option
- }
- return m.DB.Exec(createIndexSQL, values...).Error
- }
- return fmt.Errorf("failed to create index with name %v", name)
- })
- }
- func (m Migrator) DropIndex(value interface{}, name string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- if idx := stmt.Schema.LookIndex(name); idx != nil {
- name = idx.Name
- }
- return m.DB.Exec("DROP INDEX ? ON ?", clause.Column{Name: name}, m.CurrentTable(stmt)).Error
- })
- }
- func (m Migrator) HasIndex(value interface{}, name string) bool {
- var count int64
- m.RunWithValue(value, func(stmt *gorm.Statement) error {
- currentDatabase := m.DB.Migrator().CurrentDatabase()
- if idx := stmt.Schema.LookIndex(name); idx != nil {
- name = idx.Name
- }
- return m.DB.Raw(
- "SELECT count(*) FROM information_schema.statistics WHERE table_schema = ? AND table_name = ? AND index_name = ?",
- currentDatabase, stmt.Table, name,
- ).Row().Scan(&count)
- })
- return count > 0
- }
- func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
- return m.RunWithValue(value, func(stmt *gorm.Statement) error {
- return m.DB.Exec(
- "ALTER TABLE ? RENAME INDEX ? TO ?",
- m.CurrentTable(stmt), clause.Column{Name: oldName}, clause.Column{Name: newName},
- ).Error
- })
- }
- func (m Migrator) CurrentDatabase() (name string) {
- m.DB.Raw("SELECT DATABASE()").Row().Scan(&name)
- return
- }
- // ReorderModels reorder models according to constraint dependencies
- func (m Migrator) ReorderModels(values []interface{}, autoAdd bool) (results []interface{}) {
- type Dependency struct {
- *gorm.Statement
- Depends []*schema.Schema
- }
- var (
- modelNames, orderedModelNames []string
- orderedModelNamesMap = map[string]bool{}
- parsedSchemas = map[*schema.Schema]bool{}
- valuesMap = map[string]Dependency{}
- insertIntoOrderedList func(name string)
- parseDependence func(value interface{}, addToList bool)
- )
- parseDependence = func(value interface{}, addToList bool) {
- dep := Dependency{
- Statement: &gorm.Statement{DB: m.DB, Dest: value},
- }
- beDependedOn := map[*schema.Schema]bool{}
- if err := dep.Parse(value); err != nil {
- m.DB.Logger.Error(context.Background(), "failed to parse value %#v, got error %v", value, err)
- }
- if _, ok := parsedSchemas[dep.Statement.Schema]; ok {
- return
- }
- parsedSchemas[dep.Statement.Schema] = true
- for _, rel := range dep.Schema.Relationships.Relations {
- if c := rel.ParseConstraint(); c != nil && c.Schema == dep.Statement.Schema && c.Schema != c.ReferenceSchema {
- dep.Depends = append(dep.Depends, c.ReferenceSchema)
- }
- if rel.Type == schema.HasOne || rel.Type == schema.HasMany {
- beDependedOn[rel.FieldSchema] = true
- }
- if rel.JoinTable != nil {
- // append join value
- defer func(rel *schema.Relationship, joinValue interface{}) {
- if !beDependedOn[rel.FieldSchema] {
- dep.Depends = append(dep.Depends, rel.FieldSchema)
- } else {
- fieldValue := reflect.New(rel.FieldSchema.ModelType).Interface()
- parseDependence(fieldValue, autoAdd)
- }
- parseDependence(joinValue, autoAdd)
- }(rel, reflect.New(rel.JoinTable.ModelType).Interface())
- }
- }
- valuesMap[dep.Schema.Table] = dep
- if addToList {
- modelNames = append(modelNames, dep.Schema.Table)
- }
- }
- insertIntoOrderedList = func(name string) {
- if _, ok := orderedModelNamesMap[name]; ok {
- return // avoid loop
- }
- orderedModelNamesMap[name] = true
- if autoAdd {
- dep := valuesMap[name]
- for _, d := range dep.Depends {
- if _, ok := valuesMap[d.Table]; ok {
- insertIntoOrderedList(d.Table)
- } else {
- parseDependence(reflect.New(d.ModelType).Interface(), autoAdd)
- insertIntoOrderedList(d.Table)
- }
- }
- }
- orderedModelNames = append(orderedModelNames, name)
- }
- for _, value := range values {
- if v, ok := value.(string); ok {
- results = append(results, v)
- } else {
- parseDependence(value, true)
- }
- }
- for _, name := range modelNames {
- insertIntoOrderedList(name)
- }
- for _, name := range orderedModelNames {
- results = append(results, valuesMap[name].Statement.Dest)
- }
- return
- }
- func (m Migrator) CurrentTable(stmt *gorm.Statement) interface{} {
- if stmt.TableExpr != nil {
- return *stmt.TableExpr
- }
- return clause.Table{Name: stmt.Table}
- }
|