github.com/gogf/gf@v1.16.9/database/gdb/gdb_core.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  
     8  package gdb
     9  
    10  import (
    11  	"context"
    12  	"database/sql"
    13  	"fmt"
    14  	"github.com/gogf/gf/errors/gcode"
    15  	"github.com/gogf/gf/internal/intlog"
    16  	"reflect"
    17  	"strings"
    18  
    19  	"github.com/gogf/gf/errors/gerror"
    20  	"github.com/gogf/gf/internal/utils"
    21  	"github.com/gogf/gf/text/gstr"
    22  
    23  	"github.com/gogf/gf/container/gvar"
    24  	"github.com/gogf/gf/text/gregex"
    25  	"github.com/gogf/gf/util/gconv"
    26  )
    27  
    28  // GetCore returns the underlying *Core object.
    29  func (c *Core) GetCore() *Core {
    30  	return c
    31  }
    32  
    33  // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
    34  // of current DB object and with given context in it.
    35  // Note that this returned DB object can be used only once, so do not assign it to
    36  // a global or package variable for long using.
    37  func (c *Core) Ctx(ctx context.Context) DB {
    38  	if ctx == nil {
    39  		return c.db
    40  	}
    41  	ctx = context.WithValue(ctx, ctxStrictKeyName, 1)
    42  	// It makes a shallow copy of current db and changes its context for next chaining operation.
    43  	var (
    44  		err        error
    45  		newCore    = &Core{}
    46  		configNode = c.db.GetConfig()
    47  	)
    48  	*newCore = *c
    49  	newCore.ctx = ctx
    50  	// It creates a new DB object, which is commonly a wrapper for object `Core`.
    51  	newCore.db, err = driverMap[configNode.Type].New(newCore, configNode)
    52  	if err != nil {
    53  		// It is really a serious error here.
    54  		// Do not let it continue.
    55  		panic(err)
    56  	}
    57  	return newCore.db
    58  }
    59  
    60  // GetCtx returns the context for current DB.
    61  // It returns `context.Background()` is there's no context previously set.
    62  func (c *Core) GetCtx() context.Context {
    63  	if c.ctx != nil {
    64  		return c.ctx
    65  	}
    66  	return context.TODO()
    67  }
    68  
    69  // GetCtxTimeout returns the context and cancel function for specified timeout type.
    70  func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) {
    71  	if ctx == nil {
    72  		ctx = c.GetCtx()
    73  	} else {
    74  		ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil)
    75  	}
    76  	switch timeoutType {
    77  	case ctxTimeoutTypeExec:
    78  		if c.db.GetConfig().ExecTimeout > 0 {
    79  			return context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout)
    80  		}
    81  	case ctxTimeoutTypeQuery:
    82  		if c.db.GetConfig().QueryTimeout > 0 {
    83  			return context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
    84  		}
    85  	case ctxTimeoutTypePrepare:
    86  		if c.db.GetConfig().PrepareTimeout > 0 {
    87  			return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
    88  		}
    89  	default:
    90  		panic(gerror.NewCodef(gcode.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType))
    91  	}
    92  	return ctx, func() {}
    93  }
    94  
    95  // Close closes the database and prevents new queries from starting.
    96  // Close then waits for all queries that have started processing on the server
    97  // to finish.
    98  //
    99  // It is rare to Close a DB, as the DB handle is meant to be
   100  // long-lived and shared between many goroutines.
   101  func (c *Core) Close(ctx context.Context) (err error) {
   102  	c.links.LockFunc(func(m map[string]interface{}) {
   103  		for k, v := range m {
   104  			if db, ok := v.(*sql.DB); ok {
   105  				err = db.Close()
   106  				intlog.Printf(ctx, `close link: %s, err: %v`, k, err)
   107  				if err != nil {
   108  					return
   109  				}
   110  				delete(m, k)
   111  			}
   112  		}
   113  	})
   114  	return
   115  }
   116  
   117  // Master creates and returns a connection from master node if master-slave configured.
   118  // It returns the default connection if master-slave not configured.
   119  func (c *Core) Master(schema ...string) (*sql.DB, error) {
   120  	useSchema := ""
   121  	if len(schema) > 0 && schema[0] != "" {
   122  		useSchema = schema[0]
   123  	} else {
   124  		useSchema = c.schema.Val()
   125  	}
   126  	return c.getSqlDb(true, useSchema)
   127  }
   128  
   129  // Slave creates and returns a connection from slave node if master-slave configured.
   130  // It returns the default connection if master-slave not configured.
   131  func (c *Core) Slave(schema ...string) (*sql.DB, error) {
   132  	useSchema := ""
   133  	if len(schema) > 0 && schema[0] != "" {
   134  		useSchema = schema[0]
   135  	} else {
   136  		useSchema = c.schema.Val()
   137  	}
   138  	return c.getSqlDb(false, useSchema)
   139  }
   140  
   141  // GetAll queries and returns data records from database.
   142  func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) {
   143  	return c.db.DoGetAll(c.GetCtx(), nil, sql, args...)
   144  }
   145  
   146  // DoGetAll queries and returns data records from database.
   147  func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) {
   148  	rows, err := c.db.DoQuery(ctx, link, sql, args...)
   149  	if err != nil || rows == nil {
   150  		return nil, err
   151  	}
   152  	defer rows.Close()
   153  	return c.convertRowsToResult(rows)
   154  }
   155  
   156  // GetOne queries and returns one record from database.
   157  func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) {
   158  	list, err := c.db.GetAll(sql, args...)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	if len(list) > 0 {
   163  		return list[0], nil
   164  	}
   165  	return nil, nil
   166  }
   167  
   168  // GetArray queries and returns data values as slice from database.
   169  // Note that if there are multiple columns in the result, it returns just one column values randomly.
   170  func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) {
   171  	all, err := c.db.DoGetAll(c.GetCtx(), nil, sql, args...)
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  	return all.Array(), nil
   176  }
   177  
   178  // GetStruct queries one record from database and converts it to given struct.
   179  // The parameter `pointer` should be a pointer to struct.
   180  func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error {
   181  	one, err := c.db.GetOne(sql, args...)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	return one.Struct(pointer)
   186  }
   187  
   188  // GetStructs queries records from database and converts them to given struct.
   189  // The parameter `pointer` should be type of struct slice: []struct/[]*struct.
   190  func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error {
   191  	all, err := c.db.GetAll(sql, args...)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	return all.Structs(pointer)
   196  }
   197  
   198  // GetScan queries one or more records from database and converts them to given struct or
   199  // struct array.
   200  //
   201  // If parameter `pointer` is type of struct pointer, it calls GetStruct internally for
   202  // the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally
   203  // for conversion.
   204  func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error {
   205  	t := reflect.TypeOf(pointer)
   206  	k := t.Kind()
   207  	if k != reflect.Ptr {
   208  		return fmt.Errorf("params should be type of pointer, but got: %v", k)
   209  	}
   210  	k = t.Elem().Kind()
   211  	switch k {
   212  	case reflect.Array, reflect.Slice:
   213  		return c.db.GetCore().GetStructs(pointer, sql, args...)
   214  	case reflect.Struct:
   215  		return c.db.GetCore().GetStruct(pointer, sql, args...)
   216  	}
   217  	return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
   218  }
   219  
   220  // GetValue queries and returns the field value from database.
   221  // The sql should queries only one field from database, or else it returns only one
   222  // field of the result.
   223  func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) {
   224  	one, err := c.db.GetOne(sql, args...)
   225  	if err != nil {
   226  		return gvar.New(nil), err
   227  	}
   228  	for _, v := range one {
   229  		return v, nil
   230  	}
   231  	return gvar.New(nil), nil
   232  }
   233  
   234  // GetCount queries and returns the count from database.
   235  func (c *Core) GetCount(sql string, args ...interface{}) (int, error) {
   236  	// If the query fields do not contains function "COUNT",
   237  	// it replaces the sql string and adds the "COUNT" function to the fields.
   238  	if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) {
   239  		sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql)
   240  	}
   241  	value, err := c.db.GetValue(sql, args...)
   242  	if err != nil {
   243  		return 0, err
   244  	}
   245  	return value.Int(), nil
   246  }
   247  
   248  // Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement.
   249  func (c *Core) Union(unions ...*Model) *Model {
   250  	return c.doUnion(unionTypeNormal, unions...)
   251  }
   252  
   253  // UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement.
   254  func (c *Core) UnionAll(unions ...*Model) *Model {
   255  	return c.doUnion(unionTypeAll, unions...)
   256  }
   257  
   258  func (c *Core) doUnion(unionType int, unions ...*Model) *Model {
   259  	var (
   260  		unionTypeStr   string
   261  		composedSqlStr string
   262  		composedArgs   = make([]interface{}, 0)
   263  	)
   264  	if unionType == unionTypeAll {
   265  		unionTypeStr = "UNION ALL"
   266  	} else {
   267  		unionTypeStr = "UNION"
   268  	}
   269  	for _, v := range unions {
   270  		sqlWithHolder, holderArgs := v.getFormattedSqlAndArgs(queryTypeNormal, false)
   271  		if composedSqlStr == "" {
   272  			composedSqlStr += fmt.Sprintf(`(%s)`, sqlWithHolder)
   273  		} else {
   274  			composedSqlStr += fmt.Sprintf(` %s (%s)`, unionTypeStr, sqlWithHolder)
   275  		}
   276  		composedArgs = append(composedArgs, holderArgs...)
   277  	}
   278  	return c.db.Raw(composedSqlStr, composedArgs...)
   279  }
   280  
   281  // PingMaster pings the master node to check authentication or keeps the connection alive.
   282  func (c *Core) PingMaster() error {
   283  	if master, err := c.db.Master(); err != nil {
   284  		return err
   285  	} else {
   286  		return master.Ping()
   287  	}
   288  }
   289  
   290  // PingSlave pings the slave node to check authentication or keeps the connection alive.
   291  func (c *Core) PingSlave() error {
   292  	if slave, err := c.db.Slave(); err != nil {
   293  		return err
   294  	} else {
   295  		return slave.Ping()
   296  	}
   297  }
   298  
   299  // Insert does "INSERT INTO ..." statement for the table.
   300  // If there's already one unique record of the data in the table, it returns error.
   301  //
   302  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   303  // Eg:
   304  // Data(g.Map{"uid": 10000, "name":"john"})
   305  // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
   306  //
   307  // The parameter `batch` specifies the batch operation count when given data is slice.
   308  func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
   309  	if len(batch) > 0 {
   310  		return c.Model(table).Data(data).Batch(batch[0]).Insert()
   311  	}
   312  	return c.Model(table).Data(data).Insert()
   313  }
   314  
   315  // InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
   316  // If there's already one unique record of the data in the table, it ignores the inserting.
   317  //
   318  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   319  // Eg:
   320  // Data(g.Map{"uid": 10000, "name":"john"})
   321  // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
   322  //
   323  // The parameter `batch` specifies the batch operation count when given data is slice.
   324  func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
   325  	if len(batch) > 0 {
   326  		return c.Model(table).Data(data).Batch(batch[0]).InsertIgnore()
   327  	}
   328  	return c.Model(table).Data(data).InsertIgnore()
   329  }
   330  
   331  // InsertAndGetId performs action Insert and returns the last insert id that automatically generated.
   332  func (c *Core) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) {
   333  	if len(batch) > 0 {
   334  		return c.Model(table).Data(data).Batch(batch[0]).InsertAndGetId()
   335  	}
   336  	return c.Model(table).Data(data).InsertAndGetId()
   337  }
   338  
   339  // Replace does "REPLACE INTO ..." statement for the table.
   340  // If there's already one unique record of the data in the table, it deletes the record
   341  // and inserts a new one.
   342  //
   343  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   344  // Eg:
   345  // Data(g.Map{"uid": 10000, "name":"john"})
   346  // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
   347  //
   348  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   349  // If given data is type of slice, it then does batch replacing, and the optional parameter
   350  // `batch` specifies the batch operation count.
   351  func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
   352  	if len(batch) > 0 {
   353  		return c.Model(table).Data(data).Batch(batch[0]).Replace()
   354  	}
   355  	return c.Model(table).Data(data).Replace()
   356  }
   357  
   358  // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table.
   359  // It updates the record if there's primary or unique index in the saving data,
   360  // or else it inserts a new record into the table.
   361  //
   362  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   363  // Eg:
   364  // Data(g.Map{"uid": 10000, "name":"john"})
   365  // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
   366  //
   367  // If given data is type of slice, it then does batch saving, and the optional parameter
   368  // `batch` specifies the batch operation count.
   369  func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
   370  	if len(batch) > 0 {
   371  		return c.Model(table).Data(data).Batch(batch[0]).Save()
   372  	}
   373  	return c.Model(table).Data(data).Save()
   374  }
   375  
   376  // DoInsert inserts or updates data forF given table.
   377  // This function is usually used for custom interface definition, you do not need call it manually.
   378  // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
   379  // Eg:
   380  // Data(g.Map{"uid": 10000, "name":"john"})
   381  // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
   382  //
   383  // The parameter `option` values are as follows:
   384  // 0: insert:  just insert, if there's unique/primary key in the data, it returns error;
   385  // 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
   386  // 2: save:    if there's unique/primary key in the data, it updates it or else inserts a new one;
   387  // 3: ignore:  if there's unique/primary key in the data, it ignores the inserting;
   388  func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
   389  	var (
   390  		keys           []string      // Field names.
   391  		values         []string      // Value holder string array, like: (?,?,?)
   392  		params         []interface{} // Values that will be committed to underlying database driver.
   393  		onDuplicateStr string        // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
   394  	)
   395  	// Handle the field names and place holders.
   396  	for k, _ := range list[0] {
   397  		keys = append(keys, k)
   398  	}
   399  	// Prepare the batch result pointer.
   400  	var (
   401  		charL, charR = c.db.GetChars()
   402  		batchResult  = new(SqlResult)
   403  		keysStr      = charL + strings.Join(keys, charR+","+charL) + charR
   404  		operation    = GetInsertOperationByOption(option.InsertOption)
   405  	)
   406  	if option.InsertOption == insertOptionSave {
   407  		onDuplicateStr = c.formatOnDuplicate(keys, option)
   408  	}
   409  	var (
   410  		listLength  = len(list)
   411  		valueHolder = make([]string, 0)
   412  	)
   413  	for i := 0; i < listLength; i++ {
   414  		values = values[:0]
   415  		// Note that the map type is unordered,
   416  		// so it should use slice+key to retrieve the value.
   417  		for _, k := range keys {
   418  			if s, ok := list[i][k].(Raw); ok {
   419  				values = append(values, gconv.String(s))
   420  			} else {
   421  				values = append(values, "?")
   422  				params = append(params, list[i][k])
   423  			}
   424  		}
   425  		valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
   426  		// Batch package checks: It meets the batch number or it is the last element.
   427  		if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
   428  			r, err := c.db.DoExec(ctx, link, fmt.Sprintf(
   429  				"%s INTO %s(%s) VALUES%s %s",
   430  				operation, c.QuotePrefixTableName(table), keysStr,
   431  				gstr.Join(valueHolder, ","),
   432  				onDuplicateStr,
   433  			), params...)
   434  			if err != nil {
   435  				return r, err
   436  			}
   437  			if n, err := r.RowsAffected(); err != nil {
   438  				return r, err
   439  			} else {
   440  				batchResult.result = r
   441  				batchResult.affected += n
   442  			}
   443  			params = params[:0]
   444  			valueHolder = valueHolder[:0]
   445  		}
   446  	}
   447  	return batchResult, nil
   448  }
   449  
   450  func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string {
   451  	var (
   452  		onDuplicateStr string
   453  	)
   454  	if option.OnDuplicateStr != "" {
   455  		onDuplicateStr = option.OnDuplicateStr
   456  	} else if len(option.OnDuplicateMap) > 0 {
   457  		for k, v := range option.OnDuplicateMap {
   458  			if len(onDuplicateStr) > 0 {
   459  				onDuplicateStr += ","
   460  			}
   461  			switch v.(type) {
   462  			case Raw, *Raw:
   463  				onDuplicateStr += fmt.Sprintf(
   464  					"%s=%s",
   465  					c.QuoteWord(k),
   466  					v,
   467  				)
   468  			default:
   469  				onDuplicateStr += fmt.Sprintf(
   470  					"%s=VALUES(%s)",
   471  					c.QuoteWord(k),
   472  					c.QuoteWord(gconv.String(v)),
   473  				)
   474  			}
   475  		}
   476  	} else {
   477  		for _, column := range columns {
   478  			// If it's SAVE operation, do not automatically update the creating time.
   479  			if c.isSoftCreatedFieldName(column) {
   480  				continue
   481  			}
   482  			if len(onDuplicateStr) > 0 {
   483  				onDuplicateStr += ","
   484  			}
   485  			onDuplicateStr += fmt.Sprintf(
   486  				"%s=VALUES(%s)",
   487  				c.QuoteWord(column),
   488  				c.QuoteWord(column),
   489  			)
   490  		}
   491  	}
   492  	return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr)
   493  }
   494  
   495  // Update does "UPDATE ... " statement for the table.
   496  //
   497  // The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
   498  // Eg: "uid=10000", "uid", 10000, g.Map{"uid": 10000, "name":"john"}
   499  //
   500  // The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
   501  // It is commonly used with parameter `args`.
   502  // Eg:
   503  // "uid=10000",
   504  // "uid", 10000
   505  // "money>? AND name like ?", 99999, "vip_%"
   506  // "status IN (?)", g.Slice{1,2,3}
   507  // "age IN(?,?)", 18, 50
   508  // User{ Id : 1, UserName : "john"}
   509  func (c *Core) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
   510  	return c.Model(table).Data(data).Where(condition, args...).Update()
   511  }
   512  
   513  // DoUpdate does "UPDATE ... " statement for the table.
   514  // This function is usually used for custom interface definition, you do not need call it manually.
   515  func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
   516  	table = c.QuotePrefixTableName(table)
   517  	var (
   518  		rv   = reflect.ValueOf(data)
   519  		kind = rv.Kind()
   520  	)
   521  	if kind == reflect.Ptr {
   522  		rv = rv.Elem()
   523  		kind = rv.Kind()
   524  	}
   525  	var (
   526  		params  []interface{}
   527  		updates = ""
   528  	)
   529  	switch kind {
   530  	case reflect.Map, reflect.Struct:
   531  		var (
   532  			fields         []string
   533  			dataMap        = ConvertDataForTableRecord(data)
   534  			counterHandler = func(column string, counter Counter) {
   535  				if counter.Value != 0 {
   536  					var (
   537  						column    = c.QuoteWord(column)
   538  						columnRef = c.QuoteWord(counter.Field)
   539  						columnVal = counter.Value
   540  						operator  = "+"
   541  					)
   542  					if columnVal < 0 {
   543  						operator = "-"
   544  						columnVal = -columnVal
   545  					}
   546  					fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator))
   547  					params = append(params, columnVal)
   548  				}
   549  			}
   550  		)
   551  
   552  		for k, v := range dataMap {
   553  			switch value := v.(type) {
   554  			case *Counter:
   555  				counterHandler(k, *value)
   556  
   557  			case Counter:
   558  				counterHandler(k, value)
   559  
   560  			default:
   561  				if s, ok := v.(Raw); ok {
   562  					fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s))
   563  				} else {
   564  					fields = append(fields, c.QuoteWord(k)+"=?")
   565  					params = append(params, v)
   566  				}
   567  			}
   568  		}
   569  		updates = strings.Join(fields, ",")
   570  
   571  	default:
   572  		updates = gconv.String(data)
   573  	}
   574  	if len(updates) == 0 {
   575  		return nil, gerror.NewCode(gcode.CodeMissingParameter, "data cannot be empty")
   576  	}
   577  	if len(params) > 0 {
   578  		args = append(params, args...)
   579  	}
   580  	// If no link passed, it then uses the master link.
   581  	if link == nil {
   582  		if link, err = c.MasterLink(); err != nil {
   583  			return nil, err
   584  		}
   585  	}
   586  	return c.db.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...)
   587  }
   588  
   589  // Delete does "DELETE FROM ... " statement for the table.
   590  //
   591  // The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
   592  // It is commonly used with parameter `args`.
   593  // Eg:
   594  // "uid=10000",
   595  // "uid", 10000
   596  // "money>? AND name like ?", 99999, "vip_%"
   597  // "status IN (?)", g.Slice{1,2,3}
   598  // "age IN(?,?)", 18, 50
   599  // User{ Id : 1, UserName : "john"}
   600  func (c *Core) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
   601  	return c.Model(table).Where(condition, args...).Delete()
   602  }
   603  
   604  // DoDelete does "DELETE FROM ... " statement for the table.
   605  // This function is usually used for custom interface definition, you do not need call it manually.
   606  func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
   607  	if link == nil {
   608  		if link, err = c.MasterLink(); err != nil {
   609  			return nil, err
   610  		}
   611  	}
   612  	table = c.QuotePrefixTableName(table)
   613  	return c.db.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
   614  }
   615  
   616  // convertRowsToResult converts underlying data record type sql.Rows to Result type.
   617  func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
   618  	if !rows.Next() {
   619  		return nil, nil
   620  	}
   621  	// Column names and types.
   622  	columns, err := rows.ColumnTypes()
   623  	if err != nil {
   624  		return nil, err
   625  	}
   626  	columnTypes := make([]string, len(columns))
   627  	columnNames := make([]string, len(columns))
   628  	for k, v := range columns {
   629  		columnTypes[k] = v.DatabaseTypeName()
   630  		columnNames[k] = v.Name()
   631  	}
   632  	var (
   633  		values   = make([]interface{}, len(columnNames))
   634  		result   = make(Result, 0)
   635  		scanArgs = make([]interface{}, len(values))
   636  	)
   637  	for i := range values {
   638  		scanArgs[i] = &values[i]
   639  	}
   640  	for {
   641  		if err := rows.Scan(scanArgs...); err != nil {
   642  			return result, err
   643  		}
   644  		record := Record{}
   645  		for i, value := range values {
   646  			if value == nil {
   647  				record[columnNames[i]] = gvar.New(nil)
   648  			} else {
   649  				record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
   650  			}
   651  		}
   652  		result = append(result, record)
   653  		if !rows.Next() {
   654  			break
   655  		}
   656  	}
   657  	return result, nil
   658  }
   659  
   660  // MarshalJSON implements the interface MarshalJSON for json.Marshal.
   661  // It just returns the pointer address.
   662  //
   663  // Note that this interface implements mainly for workaround for a json infinite loop bug
   664  // of Golang version < v1.14.
   665  func (c *Core) MarshalJSON() ([]byte, error) {
   666  	return []byte(fmt.Sprintf(`%+v`, c)), nil
   667  }
   668  
   669  // writeSqlToLogger outputs the sql object to logger.
   670  // It is enabled only if configuration "debug" is true.
   671  func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
   672  	var transactionIdStr string
   673  	if sql.IsTransaction {
   674  		if v := ctx.Value(transactionIdForLoggerCtx); v != nil {
   675  			transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64))
   676  		}
   677  	}
   678  	s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format)
   679  	if sql.Error != nil {
   680  		s += "\nError: " + sql.Error.Error()
   681  		c.logger.Ctx(ctx).Error(s)
   682  	} else {
   683  		c.logger.Ctx(ctx).Debug(s)
   684  	}
   685  }
   686  
   687  // HasTable determine whether the table name exists in the database.
   688  func (c *Core) HasTable(name string) (bool, error) {
   689  	tableList, err := c.db.Tables(c.GetCtx())
   690  	if err != nil {
   691  		return false, err
   692  	}
   693  	for _, table := range tableList {
   694  		if table == name {
   695  			return true, nil
   696  		}
   697  	}
   698  	return false, nil
   699  }
   700  
   701  // isSoftCreatedFieldName checks and returns whether given filed name is an automatic-filled created time.
   702  func (c *Core) isSoftCreatedFieldName(fieldName string) bool {
   703  	if fieldName == "" {
   704  		return false
   705  	}
   706  	if config := c.db.GetConfig(); config.CreatedAt != "" {
   707  		if utils.EqualFoldWithoutChars(fieldName, config.CreatedAt) {
   708  			return true
   709  		}
   710  		return gstr.InArray(append([]string{config.CreatedAt}, createdFiledNames...), fieldName)
   711  	}
   712  	for _, v := range createdFiledNames {
   713  		if utils.EqualFoldWithoutChars(fieldName, v) {
   714  			return true
   715  		}
   716  	}
   717  	return false
   718  }