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  }