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
}
-
一旦
clone
为 1 或 2,在首次调用getInstance()
后,clone
都为变成 0;而 clone 为 0,调用getInstance()
时会陷入死循环,即无限为 0。 - 只有两个函数能修改,将
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)
}
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!