gorm阅读2

创建orm db handle 后, 对schema migrate

// Migrate the schema
dot1xDb.AutoMigrate(&dot1xDataItem{})

初始化创建open orm时;一开始dot1xDb的clone变量为1

tmp := sqlite.Open(dot1xpath)
dot1xDb, err := gorm.Open(tmp, &gorm.Config{})

根据getInstace 代码分析可知:一开始clone > 0,就会返回新的clone = 0的tx;如果clone<= 0 就直接使用当前db

链式方法在每次调用时,内部会先调用 getInstance() 函数,该函数的实现如下

func (db *DB) getInstance() *DB {
	if db.clone > 0 {
        // 创建新的 DB 实例,由于 Go 变量零值的原因,新的 DB.clone = 0 
		tx := &DB{Config: db.Config, Error: db.Error}

		if db.clone == 1 {
			// 创建一个全新的 statement
			tx.Statement = &Statement{
				DB:       tx,
				ConnPool: db.Statement.ConnPool,
				Context:  db.Statement.Context,
				Clauses:  map[string]clause.Clause{},
				Vars:     make([]interface{}, 0, 8),
			}
		} else {
            // 复用原来的的 statement,即 SQL 语句会复用
			tx.Statement = db.Statement.clone()
			tx.Statement.DB = tx
		}

		return tx
	}

    // clone = 0,直接返回原 db
	return db
}
  1. 一旦 clone 为 1 或 2,在首次调用 getInstance() 后,clone 都为变成 0;而 clone 为 0,调用 getInstance() 时会陷入死循环,即无限为 0。

  2. 只有两个函数能修改,将 clone 从 0 变 1 或 2:WithContext() 和 Session()WithContext() 内部调用 Session())。Session() 可以根据传入的 config.NewDB 的值来决定把 clone 变成 1 还是 2

这些涉及到orm的链式操作!!

在 链式方法终结方法之后, GORM 返回一个初始化的 *gorm.DB 实例,实例不能安全地重复使用,并且新生成的 SQL 可能会被先前的条件污染,例如:

queryDB := DB.Where("name = ?", "jinzhu")

queryDB.Where("age > ?", 10).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age > 10

queryDB.Where("age > ?", 20).First(&user2)
// SELECT * FROM users WHERE name = "jinzhu" AND age > 10 AND age > 20

为了重新使用初始化的 *gorm.DB 实例, 您可以使用 新建会话方法 创建一个可共享的 *gorm.DB, 例如:

queryDB := DB.Where("name = ?", "jinzhu").Session(&gorm.Session{})

queryDB.Where("age > ?", 10).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age > 10

queryDB.Where("age > ?", 20).First(&user2)
// SELECT * FROM users WHERE name = "jinzhu" AND age > 20

为什么会这样?因为getInstance() 会根据clone值是返回新的db还是使用以前的db

回到正题:AutoMigrate调用为:

//migrator.go
// Migrator returns migrator package gorm
func (db *DB) Migrator() Migrator {
	tx := db.getInstance()

	// apply scopes to migrator
	for len(tx.Statement.scopes) > 0 {
		tx = tx.executeScopes()
	}

	return tx.Dialector.Migrator(tx.Session(&Session{}))-----》
}

// AutoMigrate run auto migration for given models
func (db *DB) AutoMigrate(dst ...interface{}) error {
	return db.Migrator().AutoMigrate(dst...)  ----
}

//sqlite.go
func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator {
	return Migrator{migrator.Migrator{Config: migrator.Config{
		DB:                          db,
		Dialector:                   dialector,
		CreateIndexAfterCreateTable: true,
	}}}
}


解析:
db.Migrator()--->(db *DB) Migrator()--->调用dialector.Migrator也就是执行func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator
其返回的是gorm.Migrator接口但是实体是
//package sqlite //见sqlite的实现 "gorm.io/gorm/migrator"
type Migrator struct { 
	migrator.Migrator 
}
// Migrator m struct  package migrator
type Migrator struct {
	Config
}
db.Migrator().AutoMigrate(dst...)---->gorm.Migrator.AutoMigrate(dst...)--->
也就调用package gorm 的 func (m Migrator) AutoMigrate(values ...interface{}) error 函数

其核心在ReorderModels   其会调用 ParseWithSpecialTableName  创建表项

func (stmt *Statement) ParseWithSpecialTableName(value interface{}, specialTableName string) (err error) {
	if stmt.Schema, err = schema.ParseWithSpecialTableName(value, stmt.DB.cacheStore, stmt.DB.NamingStrategy, specialTableName); err == nil && stmt.Table == "" {
		if tables := strings.Split(stmt.Schema.Table, "."); len(tables) == 2 {
			stmt.TableExpr = &clause.Expr{SQL: stmt.Quote(stmt.Schema.Table)}
			stmt.Table = tables[1]
			return
		}

		stmt.Table = stmt.Schema.Table
	}
	return err
}

解析表项后,组装sql 语句然后err = tx.Exec(createTableSQL, values...).Error  执行创建

字段权限

GORM 内置了一些 Tag 来控制字段级别的权限,例如只读、只写、只创建、只更新或者被忽略:

type User struct {
  Name string `gorm:"<-:create"` // allow read and create
  Name string `gorm:"<-:update"` // allow read and update
  Name string `gorm:"<-"`        // allow read and write (create and update)
  Name string `gorm:"<-:false"`  // allow read, disable write permission
  Name string `gorm:"->"`        // readonly (disable write permission unless it configured)
  Name string `gorm:"->;<-:create"` // allow read and create
  Name string `gorm:"->:false;<-:create"` // createonly (disabled read from db)
  Name string `gorm:"-"`            // ignore this field when write and read with struct
  Name string `gorm:"-:all"`        // ignore this field when write, read and migrate with struct
  Name string `gorm:"-:migration"`  // ignore this field when migrate with struct
}

这些 Tag 会在 Model 解析时跟随字段就一起被解析,信息被保存在 Schema 结构体内嵌的 Field 结构体中

type Schema struct {
	Name                      string
	ModelType                 reflect.Type
	Table                     string
	PrioritizedPrimaryField   *Field
	DBNames                   []string
	PrimaryFields             []*Field
	PrimaryFieldDBNames       []string
	Fields                    []*Field
	FieldsByName              map[string]*Field
	FieldsByDBName            map[string]*Field
	FieldsWithDefaultDBValue  []*Field
	Relationships             Relationships
    // ...
}

type Field struct {
	Name                   string
	DBName                 string
	PrimaryKey             bool
	AutoIncrement          bool
	AutoIncrementIncrement int64
	Creatable              bool
	Updatable              bool
	Readable               bool
	IgnoreMigration        bool
	HasDefaultValue        bool
	DefaultValue           string
	DefaultValueInterface  interface{}
	NotNull                bool
	Unique                 bool
	Tag                    reflect.StructTag
	TagSettings            map[string]string
	// ...
}

可以看到 Field 包含了 Creatable Updatable Readable IgnoreMigration 这些布尔类型的属性,它们会在解析过程中根据 Tag 被赋值:

func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
	// ...

	if val, ok := field.TagSettings["-"]; ok {
		val = strings.ToLower(strings.TrimSpace(val))
		switch val {
		case "-":
			field.Creatable = false
			field.Updatable = false
			field.Readable = false
			field.DataType = ""
		case "all":
			field.Creatable = false
			field.Updatable = false
			field.Readable = false
			field.DataType = ""
			field.IgnoreMigration = true
		case "migration":
			field.IgnoreMigration = true
		}
	}

	if v, ok := field.TagSettings["->"]; ok {
		field.Creatable = false
		field.Updatable = false
		if strings.ToLower(v) == "false" {
			field.Readable = false
		} else {
			field.Readable = true
		}
	}

	if v, ok := field.TagSettings["<-"]; ok {
		field.Creatable = true
		field.Updatable = true

		if v != "<-" {
			if !strings.Contains(v, "create") {
				field.Creatable = false
			}

			if !strings.Contains(v, "update") {
				field.Updatable = false
			}
		}
	}

	// ...
}

然后创建、更新等操作中会判断这些属性,达到控制字段权限的目的。例如 AutoMigrate 方法中会有建表的调用,如果某字段的 IgnoreMigration 为 true,则会跳过这个字段:

func (m Migrator) CreateTable(values ...interface{}) error {
	// ...

			for _, dbName := range stmt.Schema.DBNames {
				field := stmt.Schema.FieldsByDBName[dbName]
				if !field.IgnoreMigration {
					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 += ","
				}
			}

	// ...
}

按照当前例子使用:

type dot1xDataItem struct {
	UserName             string `gorm:"primaryKey; column:user_name"`
	ClientMacAddr        string `gorm:"primaryKey; column:client_macaddr"`
	NasMacAddr           string `gorm:"primaryKey; column:nas_macaddr"`
	TunnelType           string `gorm:"column:tunnel_type"`
	TunnelMediumType     string `gorm:"column:tunnel_medtype"`
	TunnelPrivateGroupID string `gorm:"column:tunnel_pgid"`
	FilterId             string `gorm:"column:filter_id"`
	UpdateAt             string `gorm:"column:update_at"`
}
//执行完ParseWithSpecialTableName 后schema结果为:

*gorm.io/gorm/schema.Schema {
        Name: "dot1xDataItem",
        ModelType: reflect.Type(*reflect.rtype) *{size: 128, ptrdata: 120, hash: 2033515945, tflag: tflagUncommon|tflagExtraStar|tflagNamed (7), align: 8, fieldAlign: 8, kind: 25, equal: type:.eq.main.dot1xDataItem, gcdata: *85, str: 37570, ptrToThis: 158624},
        Table: "auth1x",
        PrioritizedPrimaryField: *gorm.io/gorm/schema.Field nil,
        DBNames: []string len: 8, cap: 8, [
                "user_name",
                "client_macaddr",
                "nas_macaddr",
                "tunnel_type",
                "tunnel_medtype",
                "tunnel_pgid",
                "filter_id",
                "update_at",
        ],
        PrimaryFields: []*gorm.io/gorm/schema.Field len: 3, cap: 4, [
                *(*"gorm.io/gorm/schema.Field")(0xc0000185a0),
                *(*"gorm.io/gorm/schema.Field")(0xc000018780),
                *(*"gorm.io/gorm/schema.Field")(0xc000018960),
        ],
        PrimaryFieldDBNames: []string len: 3, cap: 4, [
                "user_name",
                "client_macaddr",
                "nas_macaddr",
        ],
        Fields: []*gorm.io/gorm/schema.Field len: 8, cap: 8, [
                *(*"gorm.io/gorm/schema.Field")(0xc0000185a0),
                *(*"gorm.io/gorm/schema.Field")(0xc000018780),
                *(*"gorm.io/gorm/schema.Field")(0xc000018960),
                *(*"gorm.io/gorm/schema.Field")(0xc000018b40),
                *(*"gorm.io/gorm/schema.Field")(0xc000018d20),
                *(*"gorm.io/gorm/schema.Field")(0xc000018f00),
                *(*"gorm.io/gorm/schema.Field")(0xc0000190e0),
                *(*"gorm.io/gorm/schema.Field")(0xc0000192c0),
        ],
        FieldsByName: map[string]*gorm.io/gorm/schema.Field [
                "UserName": *(*"gorm.io/gorm/schema.Field")(0xc0000185a0), 
                "ClientMacAddr": *(*"gorm.io/gorm/schema.Field")(0xc000018780), 
                "NasMacAddr": *(*"gorm.io/gorm/schema.Field")(0xc000018960), 
                "TunnelType": *(*"gorm.io/gorm/schema.Field")(0xc000018b40), 
                "TunnelMediumType": *(*"gorm.io/gorm/schema.Field")(0xc000018d20), 
                "TunnelPrivateGroupID": *(*"gorm.io/gorm/schema.Field")(0xc000018f00), 
                "FilterId": *(*"gorm.io/gorm/schema.Field")(0xc0000190e0), 
                "UpdateAt": *(*"gorm.io/gorm/schema.Field")(0xc0000192c0), 
        ],
        FieldsByBindName: map[string]*gorm.io/gorm/schema.Field [
                "UserName": *(*"gorm.io/gorm/schema.Field")(0xc0000185a0), 
                "ClientMacAddr": *(*"gorm.io/gorm/schema.Field")(0xc000018780), 
                "NasMacAddr": *(*"gorm.io/gorm/schema.Field")(0xc000018960), 
                "TunnelType": *(*"gorm.io/gorm/schema.Field")(0xc000018b40), 
                "TunnelMediumType": *(*"gorm.io/gorm/schema.Field")(0xc000018d20), 
                "TunnelPrivateGroupID": *(*"gorm.io/gorm/schema.Field")(0xc000018f00), 
                "FilterId": *(*"gorm.io/gorm/schema.Field")(0xc0000190e0), 
                "UpdateAt": *(*"gorm.io/gorm/schema.Field")(0xc0000192c0), 
        ],
        FieldsByDBName: map[string]*gorm.io/gorm/schema.Field [
                "user_name": *(*"gorm.io/gorm/schema.Field")(0xc0000185a0), 
                "client_macaddr": *(*"gorm.io/gorm/schema.Field")(0xc000018780), 
                "nas_macaddr": *(*"gorm.io/gorm/schema.Field")(0xc000018960), 
                "tunnel_type": *(*"gorm.io/gorm/schema.Field")(0xc000018b40), 
                "tunnel_medtype": *(*"gorm.io/gorm/schema.Field")(0xc000018d20), 
                "tunnel_pgid": *(*"gorm.io/gorm/schema.Field")(0xc000018f00), 
                "filter_id": *(*"gorm.io/gorm/schema.Field")(0xc0000190e0), 
                "update_at": *(*"gorm.io/gorm/schema.Field")(0xc0000192c0), 
        ],
        FieldsWithDefaultDBValue: []*gorm.io/gorm/schema.Field len: 0, cap: 0, nil,
        Relationships: gorm.io/gorm/schema.Relationships {
                HasOne: []*gorm.io/gorm/schema.Relationship len: 0, cap: 0, nil,
                BelongsTo: []*gorm.io/gorm/schema.Relationship len: 0, cap: 0, nil,
                HasMany: []*gorm.io/gorm/schema.Relationship len: 0, cap: 0, nil,
                Many2Many: []*gorm.io/gorm/schema.Relationship len: 0, cap: 0, nil,
                Relations: map[string]*gorm.io/gorm/schema.Relationship [],
                EmbeddedRelations: map[string]*gorm.io/gorm/schema.Relationships nil,},
        CreateClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
        QueryClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
        UpdateClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
        DeleteClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
        BeforeCreate: false,
        AfterCreate: false,
        BeforeUpdate: false,
        AfterUpdate: false,
        BeforeDelete: false,
        AfterDelete: false,
        BeforeSave: false,
        AfterSave: false,
        AfterFind: false,
        err: error nil,
        initialized: chan struct {} {
                qcount: 0,
                dataqsiz: 0,
                buf: *[0]struct struct {} [],
                elemsize: 0,
                closed: 0,
                elemtype: *runtime._type {size: 0, ptrdata: 0, hash: 3842252374, tflag: tflagExtraStar|tflagRegularMemory (10), align: 1, fieldAlign: 1, kind: 25, equal: runtime.memequal0, gcdata: *0, str: 14219, ptrToThis: 0},
                sendx: 0,
                recvx: 0,
                recvq: waitq<struct {}> {
                        first: *sudog<struct {}> nil,
                        last: *sudog<struct {}> nil,},
                sendq: waitq<struct {}> {
                        first: *sudog<struct {}> nil,
                        last: *sudog<struct {}> nil,},
                lock: runtime.mutex {
                        lockRankStruct: runtime.lockRankStruct {},
                        key: 0,},},
        namer: gorm.io/gorm/schema.Namer(gorm.io/gorm/schema.NamingStrategy) {
                TablePrefix: "",
                SingularTable: false,
                NameReplacer: gorm.io/gorm/schema.Replacer nil,
                NoLowerCase: false,
                IdentifierMaxLength: 64,},
        cacheStore: *sync.Map {
                mu: (*sync.Mutex)(0xc0001999c0),
                read: (*"sync/atomic.Pointer[sync.readOnly]")(0xc0001999c8),
                dirty: map[interface {}]*sync.entry nil,
                misses: 0,},}

 对应statment值为:

(dlv) p stmt
*gorm.io/gorm.Statement {
        DB: *gorm.io/gorm.DB {
                Config: *(*"gorm.io/gorm.Config")(0xc00011a5a0),
                Error: error nil,
                RowsAffected: 0,
                Statement: *(*"gorm.io/gorm.Statement")(0xc0001e41c0),
                clone: 2,},
        TableExpr: *gorm.io/gorm/clause.Expr nil,
        Table: "auth1x",
        Model: interface {} nil,
        Unscoped: false,
        Dest: interface {}(*main.dot1xDataItem) *{UserName: "", ClientMacAddr: "", NasMacAddr: "", TunnelType: "", TunnelMediumType: "", TunnelPrivateGroupID: "", FilterId: "", UpdateAt: ""},
        ReflectValue: reflect.Value {
                typ: *reflect.rtype nil,
                ptr: unsafe.Pointer(0x0),
                flag: 0,},
        Clauses: map[string]gorm.io/gorm/clause.Clause nil,
        BuildClauses: []string len: 0, cap: 0, nil,
        Distinct: false,
        Selects: []string len: 0, cap: 0, nil,
        Omits: []string len: 0, cap: 0, nil,
        Joins: []gorm.io/gorm.join len: 0, cap: 0, nil,
        Preloads: map[string][]interface {} nil,
        Settings: sync.Map {
                mu: (*sync.Mutex)(0xc0001e4458),
                read: (*"sync/atomic.Pointer[sync.readOnly]")(0xc0001e4460),
                dirty: map[interface {}]*sync.entry nil,
                misses: 0,},
        ConnPool: gorm.io/gorm.ConnPool nil,
        Schema: *gorm.io/gorm/schema.Schema {
                Name: "dot1xDataItem",
                ModelType: reflect.Type(*reflect.rtype) ...,
                Table: "auth1x",
                PrioritizedPrimaryField: *gorm.io/gorm/schema.Field nil,
                DBNames: []string len: 8, cap: 8, [
                        "user_name",
                        "client_macaddr",
                        "nas_macaddr",
                        "tunnel_type",
                        "tunnel_medtype",
                        "tunnel_pgid",
                        "filter_id",
                        "update_at",
                ],
                PrimaryFields: []*gorm.io/gorm/schema.Field len: 3, cap: 4, [
                        *(*"gorm.io/gorm/schema.Field")(0xc0000185a0),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018780),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018960),
                ],
                PrimaryFieldDBNames: []string len: 3, cap: 4, [
                        "user_name",
                        "client_macaddr",
                        "nas_macaddr",
                ],
                Fields: []*gorm.io/gorm/schema.Field len: 8, cap: 8, [
                        *(*"gorm.io/gorm/schema.Field")(0xc0000185a0),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018780),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018960),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018b40),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018d20),
                        *(*"gorm.io/gorm/schema.Field")(0xc000018f00),
                        *(*"gorm.io/gorm/schema.Field")(0xc0000190e0),
                        *(*"gorm.io/gorm/schema.Field")(0xc0000192c0),
                ],
                FieldsByName: map[string]*gorm.io/gorm/schema.Field [...],
                FieldsByBindName: map[string]*gorm.io/gorm/schema.Field [...],
                FieldsByDBName: map[string]*gorm.io/gorm/schema.Field [...],
                FieldsWithDefaultDBValue: []*gorm.io/gorm/schema.Field len: 0, cap: 0, nil,
                Relationships: (*"gorm.io/gorm/schema.Relationships")(0xc000018488),
                CreateClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
                QueryClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
                UpdateClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
                DeleteClauses: []gorm.io/gorm/clause.Interface len: 0, cap: 0, nil,
                BeforeCreate: false,
                AfterCreate: false,
                BeforeUpdate: false,
                AfterUpdate: false,
                BeforeDelete: false,
                AfterDelete: false,
                BeforeSave: false,
                AfterSave: false,
                AfterFind: false,
                err: error nil,
                initialized: chan struct {} {
                        qcount: 0,
                        dataqsiz: 0,
                        buf: *[0]struct struct {} [],
                        elemsize: 0,
                        closed: 1,
                        elemtype: *runtime._type {size: 0, ptrdata: 0, hash: 3842252374, tflag: tflagExtraStar|tflagRegularMemory (10), align: 1, fieldAlign: 1, kind: 25, equal: runtime.memequal0, gcdata: *0, str: 14219, ptrToThis: 0},
                        sendx: 0,
                        recvx: 0,
                        recvq: waitq<struct {}> {
                                first: *sudog<struct {}> nil,
                                last: *sudog<struct {}> nil,},
                        sendq: waitq<struct {}> {
                                first: *sudog<struct {}> nil,
                                last: *sudog<struct {}> nil,},
                        lock: runtime.mutex {
                                lockRankStruct: runtime.lockRankStruct {},
                                key: 0,},},
                namer: gorm.io/gorm/schema.Namer(gorm.io/gorm/schema.NamingStrategy) *(*"gorm.io/gorm/schema.Namer")(0xc000018580),
                cacheStore: *(*sync.Map)(0xc0001999c0),},
        Context: context.Context nil,
        RaiseErrorOnNotFound: false,
        SkipHooks: false,
        SQL: strings.Builder {
                addr: *strings.Builder nil,
                buf: []uint8 len: 0, cap: 0, nil,},
        Vars: []interface {} len: 0, cap: 0, nil,
        CurDestIndex: 0,
        attrs: []interface {} len: 0, cap: 0, nil,
        assigns: []interface {} len: 0, cap: 0, nil,
        scopes: []func(*gorm.io/gorm.DB) *gorm.io/gorm.DB len: 0, cap: 0, nil,}

当前这张schema 存储在DB.cacheStore中key 为 传入的dset 值也就是:

interface {}(*main.dot1xDataItem) *{UserName: "", ClientMacAddr: "", NasMacAddr: "", TunnelType: "", TunnelMediumType: "", TunnelPrivateGroupID: "", FilterId: "", UpdateAt: ""}

 

解析models后开始创建修改table

// AutoMigrate auto migrate values
func (m Migrator) AutoMigrate(values ...interface{}) error {
	for _, value := range m.ReorderModels(values, true) {// ---->解析Models,获取schema
		queryTx := m.DB.Session(&gorm.Session{})
		execTx := queryTx
		if m.DB.DryRun {
			queryTx.DryRun = false
			execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
		}
		if !queryTx.Migrator().HasTable(value) {
			if err := execTx.Migrator().CreateTable(value); err != nil {
                // ---->没有创建table, 根据schema 创建tab
				return err
			}
		} else {

 根据scheam 组装sql语句

func(stmt *gorm.Statement) (err error) {
			var (
				createTableSQL          = "CREATE TABLE ? ("
				values                  = []interface{}{m.CurrentTable(stmt)}
				hasPrimaryKeyInDataType bool
			)
/*
(dlv) p stmt.Schema.DBNames
[]string len: 8, cap: 8, [
        "user_name",
        "client_macaddr",
        "nas_macaddr",
        "tunnel_type",
        "tunnel_medtype",
        "tunnel_pgid",
        "filter_id",
        "update_at",
]

*/
			for _, dbName := range stmt.Schema.DBNames {
				field := stmt.Schema.FieldsByDBName[dbName]
				if !field.IgnoreMigration {
					createTableSQL += "? ?"
					hasPrimaryKeyInDataType = hasPrimaryKeyInDataType || strings.Contains(strings.ToUpper(m.DataTypeOf(field)), "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 := make([]interface{}, 0, len(stmt.Schema.PrimaryFields))
				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 err == nil {
							err = tx.Migrator().CreateIndex(value, name)
						}
					}(value, idx.Name)
				} else {
					if idx.Class != "" {
						createTableSQL += idx.Class + " "
					}
					createTableSQL += "INDEX ? ?"

					if idx.Comment != "" {
						createTableSQL += fmt.Sprintf(" COMMENT '%s'", idx.Comment)
					}

					if idx.Option != "" {
						createTableSQL += " " + idx.Option
					}

					createTableSQL += ","
					values = append(values, clause.Column{Name: idx.Name}, tx.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt))
				}
			}

			if !m.DB.DisableForeignKeyConstraintWhenMigrating && !m.DB.IgnoreRelationshipsWhenMigrating {
				for _, rel := range stmt.Schema.Relationships.Relations {
					if rel.Field.IgnoreMigration {
						continue
					}
					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)
			}

			err = tx.Exec(createTableSQL, values...).Error
			return err
		}); err != nil {
    
    //最后执行的时候 调用rawexec  gorm.io/gorm/callbacks.RawExec() ./vendor/gorm.io/gorm/callbacks/raw.go:7
    
    /*
    db *gorm.DB
    rawCallback := db.Callback().Raw()
	rawCallback.Register("gorm:raw", RawExec)
	rawCallback.Clauses = config.QueryClauses
    */
    
    /*
    // Exec executes raw sql
func (db *DB) Exec(sql string, values ...interface{}) (tx *DB) {
	tx = db.getInstance()
	tx.Statement.SQL = strings.Builder{}

	if strings.Contains(sql, "@") {
		clause.NamedExpr{SQL: sql, Vars: values}.Build(tx.Statement)
	} else {
		clause.Expr{SQL: sql, Vars: values}.Build(tx.Statement)
	}

	return tx.callbacks.Raw().Execute(tx)
}
    */

 

posted @   codestacklinuxer  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示