github.com/gogf/gf@v1.16.9/database/gdb/gdb_model_insert.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package gdb 8 9 import ( 10 "database/sql" 11 "github.com/gogf/gf/container/gset" 12 "github.com/gogf/gf/errors/gcode" 13 "reflect" 14 15 "github.com/gogf/gf/errors/gerror" 16 "github.com/gogf/gf/os/gtime" 17 "github.com/gogf/gf/text/gstr" 18 "github.com/gogf/gf/util/gconv" 19 "github.com/gogf/gf/util/gutil" 20 ) 21 22 // Batch sets the batch operation number for the model. 23 func (m *Model) Batch(batch int) *Model { 24 model := m.getModel() 25 model.batch = batch 26 return model 27 } 28 29 // Data sets the operation data for the model. 30 // The parameter `data` can be type of string/map/gmap/slice/struct/*struct, etc. 31 // Note that, it uses shallow value copying for `data` if `data` is type of map/slice 32 // to avoid changing it inside function. 33 // Eg: 34 // Data("uid=10000") 35 // Data("uid", 10000) 36 // Data("uid=? AND name=?", 10000, "john") 37 // Data(g.Map{"uid": 10000, "name":"john"}) 38 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 39 func (m *Model) Data(data ...interface{}) *Model { 40 model := m.getModel() 41 if len(data) > 1 { 42 if s := gconv.String(data[0]); gstr.Contains(s, "?") { 43 model.data = s 44 model.extraArgs = data[1:] 45 } else { 46 m := make(map[string]interface{}) 47 for i := 0; i < len(data); i += 2 { 48 m[gconv.String(data[i])] = data[i+1] 49 } 50 model.data = m 51 } 52 } else { 53 switch params := data[0].(type) { 54 case Result: 55 model.data = params.List() 56 57 case Record: 58 model.data = params.Map() 59 60 case List: 61 list := make(List, len(params)) 62 for k, v := range params { 63 list[k] = gutil.MapCopy(v) 64 } 65 model.data = list 66 67 case Map: 68 model.data = gutil.MapCopy(params) 69 70 default: 71 var ( 72 rv = reflect.ValueOf(params) 73 kind = rv.Kind() 74 ) 75 if kind == reflect.Ptr { 76 rv = rv.Elem() 77 kind = rv.Kind() 78 } 79 switch kind { 80 case reflect.Slice, reflect.Array: 81 list := make(List, rv.Len()) 82 for i := 0; i < rv.Len(); i++ { 83 list[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) 84 } 85 model.data = list 86 87 case reflect.Map: 88 model.data = ConvertDataForTableRecord(data[0]) 89 90 case reflect.Struct: 91 if v, ok := data[0].(apiInterfaces); ok { 92 var ( 93 array = v.Interfaces() 94 list = make(List, len(array)) 95 ) 96 for i := 0; i < len(array); i++ { 97 list[i] = ConvertDataForTableRecord(array[i]) 98 } 99 model.data = list 100 } else { 101 model.data = ConvertDataForTableRecord(data[0]) 102 } 103 104 default: 105 model.data = data[0] 106 } 107 } 108 } 109 return model 110 } 111 112 // OnDuplicate sets the operations when columns conflicts occurs. 113 // In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. 114 // The parameter `onDuplicate` can be type of string/Raw/*Raw/map/slice. 115 // Example: 116 // OnDuplicate("nickname, age") 117 // OnDuplicate("nickname", "age") 118 // OnDuplicate(g.Map{ 119 // "nickname": gdb.Raw("CONCAT('name_', VALUES(`nickname`))"), 120 // }) 121 // OnDuplicate(g.Map{ 122 // "nickname": "passport", 123 // }) 124 func (m *Model) OnDuplicate(onDuplicate ...interface{}) *Model { 125 model := m.getModel() 126 if len(onDuplicate) > 1 { 127 model.onDuplicate = onDuplicate 128 } else { 129 model.onDuplicate = onDuplicate[0] 130 } 131 return model 132 } 133 134 // OnDuplicateEx sets the excluding columns for operations when columns conflicts occurs. 135 // In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement. 136 // The parameter `onDuplicateEx` can be type of string/map/slice. 137 // Example: 138 // OnDuplicateEx("passport, password") 139 // OnDuplicateEx("passport", "password") 140 // OnDuplicateEx(g.Map{ 141 // "passport": "", 142 // "password": "", 143 // }) 144 func (m *Model) OnDuplicateEx(onDuplicateEx ...interface{}) *Model { 145 model := m.getModel() 146 if len(onDuplicateEx) > 1 { 147 model.onDuplicateEx = onDuplicateEx 148 } else { 149 model.onDuplicateEx = onDuplicateEx[0] 150 } 151 return model 152 } 153 154 // Insert does "INSERT INTO ..." statement for the model. 155 // The optional parameter `data` is the same as the parameter of Model.Data function, 156 // see Model.Data. 157 func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) { 158 if len(data) > 0 { 159 return m.Data(data...).Insert() 160 } 161 return m.doInsertWithOption(insertOptionDefault) 162 } 163 164 // InsertAndGetId performs action Insert and returns the last insert id that automatically generated. 165 func (m *Model) InsertAndGetId(data ...interface{}) (lastInsertId int64, err error) { 166 if len(data) > 0 { 167 return m.Data(data...).InsertAndGetId() 168 } 169 result, err := m.doInsertWithOption(insertOptionDefault) 170 if err != nil { 171 return 0, err 172 } 173 return result.LastInsertId() 174 } 175 176 // InsertIgnore does "INSERT IGNORE INTO ..." statement for the model. 177 // The optional parameter `data` is the same as the parameter of Model.Data function, 178 // see Model.Data. 179 func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) { 180 if len(data) > 0 { 181 return m.Data(data...).InsertIgnore() 182 } 183 return m.doInsertWithOption(insertOptionIgnore) 184 } 185 186 // Replace does "REPLACE INTO ..." statement for the model. 187 // The optional parameter `data` is the same as the parameter of Model.Data function, 188 // see Model.Data. 189 func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) { 190 if len(data) > 0 { 191 return m.Data(data...).Replace() 192 } 193 return m.doInsertWithOption(insertOptionReplace) 194 } 195 196 // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model. 197 // The optional parameter `data` is the same as the parameter of Model.Data function, 198 // see Model.Data. 199 // 200 // It updates the record if there's primary or unique index in the saving data, 201 // or else it inserts a new record into the table. 202 func (m *Model) Save(data ...interface{}) (result sql.Result, err error) { 203 if len(data) > 0 { 204 return m.Data(data...).Save() 205 } 206 return m.doInsertWithOption(insertOptionSave) 207 } 208 209 // doInsertWithOption inserts data with option parameter. 210 func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err error) { 211 defer func() { 212 if err == nil { 213 m.checkAndRemoveCache() 214 } 215 }() 216 if m.data == nil { 217 return nil, gerror.NewCode(gcode.CodeMissingParameter, "inserting into table with empty data") 218 } 219 var ( 220 list List 221 nowString = gtime.Now().String() 222 fieldNameCreate = m.getSoftFieldNameCreated() 223 fieldNameUpdate = m.getSoftFieldNameUpdated() 224 ) 225 newData, err := m.filterDataForInsertOrUpdate(m.data) 226 if err != nil { 227 return nil, err 228 } 229 230 // It converts any data to List type for inserting. 231 switch value := newData.(type) { 232 case Result: 233 list = value.List() 234 235 case Record: 236 list = List{value.Map()} 237 238 case List: 239 list = value 240 for i, v := range list { 241 list[i] = ConvertDataForTableRecord(v) 242 } 243 244 case Map: 245 list = List{ConvertDataForTableRecord(value)} 246 247 default: 248 var ( 249 rv = reflect.ValueOf(newData) 250 kind = rv.Kind() 251 ) 252 if kind == reflect.Ptr { 253 rv = rv.Elem() 254 kind = rv.Kind() 255 } 256 switch kind { 257 // If it's slice type, it then converts it to List type. 258 case reflect.Slice, reflect.Array: 259 list = make(List, rv.Len()) 260 for i := 0; i < rv.Len(); i++ { 261 list[i] = ConvertDataForTableRecord(rv.Index(i).Interface()) 262 } 263 264 case reflect.Map: 265 list = List{ConvertDataForTableRecord(value)} 266 267 case reflect.Struct: 268 if v, ok := value.(apiInterfaces); ok { 269 var ( 270 array = v.Interfaces() 271 ) 272 list = make(List, len(array)) 273 for i := 0; i < len(array); i++ { 274 list[i] = ConvertDataForTableRecord(array[i]) 275 } 276 } else { 277 list = List{ConvertDataForTableRecord(value)} 278 } 279 280 default: 281 return result, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported list type:%v", kind) 282 } 283 } 284 285 if len(list) < 1 { 286 return result, gerror.NewCode(gcode.CodeMissingParameter, "data list cannot be empty") 287 } 288 289 // Automatic handling for creating/updating time. 290 if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { 291 for k, v := range list { 292 if fieldNameCreate != "" { 293 v[fieldNameCreate] = nowString 294 } 295 if fieldNameUpdate != "" { 296 v[fieldNameUpdate] = nowString 297 } 298 list[k] = v 299 } 300 } 301 // Format DoInsertOption, especially for "ON DUPLICATE KEY UPDATE" statement. 302 columnNames := make([]string, 0, len(list[0])) 303 for k, _ := range list[0] { 304 columnNames = append(columnNames, k) 305 } 306 doInsertOption, err := m.formatDoInsertOption(insertOption, columnNames) 307 if err != nil { 308 return result, err 309 } 310 311 return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, doInsertOption) 312 } 313 314 func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (option DoInsertOption, err error) { 315 option = DoInsertOption{ 316 InsertOption: insertOption, 317 BatchCount: m.getBatch(), 318 } 319 if insertOption == insertOptionSave { 320 onDuplicateExKeys, err := m.formatOnDuplicateExKeys(m.onDuplicateEx) 321 if err != nil { 322 return option, err 323 } 324 var ( 325 onDuplicateExKeySet = gset.NewStrSetFrom(onDuplicateExKeys) 326 ) 327 if m.onDuplicate != nil { 328 switch m.onDuplicate.(type) { 329 case Raw, *Raw: 330 option.OnDuplicateStr = gconv.String(m.onDuplicate) 331 332 default: 333 var ( 334 reflectValue = reflect.ValueOf(m.onDuplicate) 335 reflectKind = reflectValue.Kind() 336 ) 337 for reflectKind == reflect.Ptr { 338 reflectValue = reflectValue.Elem() 339 reflectKind = reflectValue.Kind() 340 } 341 switch reflectKind { 342 case reflect.String: 343 option.OnDuplicateMap = make(map[string]interface{}) 344 for _, v := range gstr.SplitAndTrim(reflectValue.String(), ",") { 345 if onDuplicateExKeySet.Contains(v) { 346 continue 347 } 348 option.OnDuplicateMap[v] = v 349 } 350 351 case reflect.Map: 352 option.OnDuplicateMap = make(map[string]interface{}) 353 for k, v := range gconv.Map(m.onDuplicate) { 354 if onDuplicateExKeySet.Contains(k) { 355 continue 356 } 357 option.OnDuplicateMap[k] = v 358 } 359 360 case reflect.Slice, reflect.Array: 361 option.OnDuplicateMap = make(map[string]interface{}) 362 for _, v := range gconv.Strings(m.onDuplicate) { 363 if onDuplicateExKeySet.Contains(v) { 364 continue 365 } 366 option.OnDuplicateMap[v] = v 367 } 368 369 default: 370 return option, gerror.NewCodef( 371 gcode.CodeInvalidParameter, 372 `unsupported OnDuplicate parameter type "%s"`, 373 reflect.TypeOf(m.onDuplicate), 374 ) 375 } 376 } 377 } else if onDuplicateExKeySet.Size() > 0 { 378 option.OnDuplicateMap = make(map[string]interface{}) 379 for _, v := range columnNames { 380 if onDuplicateExKeySet.Contains(v) { 381 continue 382 } 383 option.OnDuplicateMap[v] = v 384 } 385 } 386 } 387 return 388 } 389 390 func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, error) { 391 if onDuplicateEx == nil { 392 return nil, nil 393 } 394 395 var ( 396 reflectValue = reflect.ValueOf(onDuplicateEx) 397 reflectKind = reflectValue.Kind() 398 ) 399 for reflectKind == reflect.Ptr { 400 reflectValue = reflectValue.Elem() 401 reflectKind = reflectValue.Kind() 402 } 403 switch reflectKind { 404 case reflect.String: 405 return gstr.SplitAndTrim(reflectValue.String(), ","), nil 406 407 case reflect.Map: 408 return gutil.Keys(onDuplicateEx), nil 409 410 case reflect.Slice, reflect.Array: 411 return gconv.Strings(onDuplicateEx), nil 412 413 default: 414 return nil, gerror.NewCodef( 415 gcode.CodeInvalidParameter, 416 `unsupported OnDuplicateEx parameter type "%s"`, 417 reflect.TypeOf(onDuplicateEx), 418 ) 419 } 420 } 421 422 func (m *Model) getBatch() int { 423 batch := defaultBatchNumber 424 if m.batch > 0 { 425 batch = m.batch 426 } 427 return batch 428 }