github.com/gogf/gf@v1.16.9/database/gdb/gdb_func.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  	"bytes"
    11  	"database/sql"
    12  	"fmt"
    13  	"github.com/gogf/gf/errors/gcode"
    14  	"reflect"
    15  	"regexp"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/gogf/gf/errors/gerror"
    20  	"github.com/gogf/gf/internal/empty"
    21  	"github.com/gogf/gf/internal/json"
    22  	"github.com/gogf/gf/internal/utils"
    23  	"github.com/gogf/gf/os/gtime"
    24  	"github.com/gogf/gf/util/gmeta"
    25  	"github.com/gogf/gf/util/gutil"
    26  
    27  	"github.com/gogf/gf/internal/structs"
    28  
    29  	"github.com/gogf/gf/text/gregex"
    30  	"github.com/gogf/gf/text/gstr"
    31  	"github.com/gogf/gf/util/gconv"
    32  )
    33  
    34  // apiString is the type assert api for String.
    35  type apiString interface {
    36  	String() string
    37  }
    38  
    39  // apiIterator is the type assert api for Iterator.
    40  type apiIterator interface {
    41  	Iterator(f func(key, value interface{}) bool)
    42  }
    43  
    44  // apiInterfaces is the type assert api for Interfaces.
    45  type apiInterfaces interface {
    46  	Interfaces() []interface{}
    47  }
    48  
    49  // apiMapStrAny is the interface support for converting struct parameter to map.
    50  type apiMapStrAny interface {
    51  	MapStrAny() map[string]interface{}
    52  }
    53  
    54  // apiTableName is the interface for retrieving table name fro struct.
    55  type apiTableName interface {
    56  	TableName() string
    57  }
    58  
    59  const (
    60  	OrmTagForStruct    = "orm"
    61  	OrmTagForUnique    = "unique"
    62  	OrmTagForPrimary   = "primary"
    63  	OrmTagForTable     = "table"
    64  	OrmTagForWith      = "with"
    65  	OrmTagForWithWhere = "where"
    66  	OrmTagForWithOrder = "order"
    67  )
    68  
    69  var (
    70  	// quoteWordReg is the regular expression object for a word check.
    71  	quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
    72  
    73  	// Priority tags for struct converting for orm field mapping.
    74  	structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
    75  )
    76  
    77  // guessPrimaryTableName parses and returns the primary table name.
    78  func (m *Model) guessPrimaryTableName(tableStr string) string {
    79  	if tableStr == "" {
    80  		return ""
    81  	}
    82  	var (
    83  		guessedTableName = ""
    84  		array1           = gstr.SplitAndTrim(tableStr, ",")
    85  		array2           = gstr.SplitAndTrim(array1[0], " ")
    86  		array3           = gstr.SplitAndTrim(array2[0], ".")
    87  	)
    88  	if len(array3) >= 2 {
    89  		guessedTableName = array3[1]
    90  	} else {
    91  		guessedTableName = array3[0]
    92  	}
    93  	charL, charR := m.db.GetChars()
    94  	if charL != "" || charR != "" {
    95  		guessedTableName = gstr.Trim(guessedTableName, charL+charR)
    96  	}
    97  	if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) {
    98  		return ""
    99  	}
   100  	return guessedTableName
   101  }
   102  
   103  // getTableNameFromOrmTag retrieves and returns the table name from struct object.
   104  func getTableNameFromOrmTag(object interface{}) string {
   105  	var tableName string
   106  	// Use the interface value.
   107  	if r, ok := object.(apiTableName); ok {
   108  		tableName = r.TableName()
   109  	}
   110  	// User meta data tag "orm".
   111  	if tableName == "" {
   112  		if ormTag := gmeta.Get(object, OrmTagForStruct); !ormTag.IsEmpty() {
   113  			match, _ := gregex.MatchString(
   114  				fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForTable),
   115  				ormTag.String(),
   116  			)
   117  			if len(match) > 1 {
   118  				tableName = match[1]
   119  			}
   120  		}
   121  	}
   122  	// Use the struct name of snake case.
   123  	if tableName == "" {
   124  		if t, err := structs.StructType(object); err != nil {
   125  			panic(err)
   126  		} else {
   127  			tableName = gstr.CaseSnakeFirstUpper(
   128  				gstr.StrEx(t.String(), "."),
   129  			)
   130  		}
   131  	}
   132  	return tableName
   133  }
   134  
   135  // ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
   136  // Note that the parameter `list` should be type of slice which contains elements of map or struct,
   137  // or else it returns an empty slice.
   138  //
   139  // The parameter `list` supports types like:
   140  // []map[string]interface{}
   141  // []map[string]sub-map
   142  // []struct
   143  // []struct:sub-struct
   144  // Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given.
   145  // See gutil.ListItemValues.
   146  func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) {
   147  	return gutil.ListItemValues(list, key, subKey...)
   148  }
   149  
   150  // ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
   151  // Note that the parameter `list` should be type of slice which contains elements of map or struct,
   152  // or else it returns an empty slice.
   153  // See gutil.ListItemValuesUnique.
   154  func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} {
   155  	return gutil.ListItemValuesUnique(list, key, subKey...)
   156  }
   157  
   158  // GetInsertOperationByOption returns proper insert option with given parameter `option`.
   159  func GetInsertOperationByOption(option int) string {
   160  	var operator string
   161  	switch option {
   162  	case insertOptionReplace:
   163  		operator = "REPLACE"
   164  	case insertOptionIgnore:
   165  		operator = "INSERT IGNORE"
   166  	default:
   167  		operator = "INSERT"
   168  	}
   169  	return operator
   170  }
   171  
   172  // ConvertDataForTableRecord is a very important function, which does converting for any data that
   173  // will be inserted into table as a record.
   174  //
   175  // The parameter `value` should be type of *map/map/*struct/struct.
   176  // It supports embedded struct definition for struct.
   177  func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
   178  	var (
   179  		rvValue reflect.Value
   180  		rvKind  reflect.Kind
   181  		data    = DataToMapDeep(value)
   182  	)
   183  	for k, v := range data {
   184  		rvValue = reflect.ValueOf(v)
   185  		rvKind = rvValue.Kind()
   186  		for rvKind == reflect.Ptr {
   187  			rvValue = rvValue.Elem()
   188  			rvKind = rvValue.Kind()
   189  		}
   190  		switch rvKind {
   191  		case reflect.Slice, reflect.Array, reflect.Map:
   192  			// It should ignore the bytes type.
   193  			if _, ok := v.([]byte); !ok {
   194  				// Convert the value to JSON.
   195  				data[k], _ = json.Marshal(v)
   196  			}
   197  
   198  		case reflect.Struct:
   199  			switch r := v.(type) {
   200  			// If the time is zero, it then updates it to nil,
   201  			// which will insert/update the value to database as "null".
   202  			case time.Time:
   203  				if r.IsZero() {
   204  					data[k] = nil
   205  				}
   206  
   207  			case gtime.Time:
   208  				if r.IsZero() {
   209  					data[k] = nil
   210  				}
   211  
   212  			case *gtime.Time:
   213  				if r.IsZero() {
   214  					data[k] = nil
   215  				}
   216  
   217  			case *time.Time:
   218  				continue
   219  
   220  			case Counter, *Counter:
   221  				continue
   222  
   223  			default:
   224  				// Use string conversion in default.
   225  				if s, ok := v.(apiString); ok {
   226  					data[k] = s.String()
   227  				} else {
   228  					// Convert the value to JSON.
   229  					data[k], _ = json.Marshal(v)
   230  				}
   231  			}
   232  		}
   233  	}
   234  	return data
   235  }
   236  
   237  // DataToMapDeep converts `value` to map type recursively(if attribute struct is embedded).
   238  // The parameter `value` should be type of *map/map/*struct/struct.
   239  // It supports embedded struct definition for struct.
   240  func DataToMapDeep(value interface{}) map[string]interface{} {
   241  	m := gconv.Map(value, structTagPriority...)
   242  	for k, v := range m {
   243  		switch v.(type) {
   244  		case time.Time, *time.Time, gtime.Time, *gtime.Time:
   245  			m[k] = v
   246  
   247  		default:
   248  			// Use string conversion in default.
   249  			if s, ok := v.(apiString); ok {
   250  				m[k] = s.String()
   251  			} else {
   252  				m[k] = v
   253  			}
   254  		}
   255  	}
   256  	return m
   257  }
   258  
   259  // doHandleTableName adds prefix string and quote chars for the table. It handles table string like:
   260  // "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut",
   261  // "user.user u", "`user`.`user` u".
   262  //
   263  // Note that, this will automatically checks the table prefix whether already added, if true it does
   264  // nothing to the table name, or else adds the prefix to the table name.
   265  func doHandleTableName(table, prefix, charLeft, charRight string) string {
   266  	var (
   267  		index  = 0
   268  		chars  = charLeft + charRight
   269  		array1 = gstr.SplitAndTrim(table, ",")
   270  	)
   271  	for k1, v1 := range array1 {
   272  		array2 := gstr.SplitAndTrim(v1, " ")
   273  		// Trim the security chars.
   274  		array2[0] = gstr.Trim(array2[0], chars)
   275  		// Check whether it has database name.
   276  		array3 := gstr.Split(gstr.Trim(array2[0]), ".")
   277  		for k, v := range array3 {
   278  			array3[k] = gstr.Trim(v, chars)
   279  		}
   280  		index = len(array3) - 1
   281  		// If the table name already has the prefix, skips the prefix adding.
   282  		if len(array3[index]) <= len(prefix) || array3[index][:len(prefix)] != prefix {
   283  			array3[index] = prefix + array3[index]
   284  		}
   285  		array2[0] = gstr.Join(array3, ".")
   286  		// Add the security chars.
   287  		array2[0] = doQuoteString(array2[0], charLeft, charRight)
   288  		array1[k1] = gstr.Join(array2, " ")
   289  	}
   290  	return gstr.Join(array1, ",")
   291  }
   292  
   293  // doQuoteWord checks given string `s` a word, if true quotes it with `charLeft` and `charRight`
   294  // and returns the quoted string; or else returns `s` without any change.
   295  func doQuoteWord(s, charLeft, charRight string) string {
   296  	if quoteWordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
   297  		return charLeft + s + charRight
   298  	}
   299  	return s
   300  }
   301  
   302  // doQuoteString quotes string with quote chars.
   303  // For example, if quote char is '`':
   304  // "user"                             => "`user`"
   305  // "user u"                           => "`user` u"
   306  // "user,user_detail"                 => "`user`,`user_detail`"
   307  // "user u, user_detail ut"           => "`user` u,`user_detail` ut"
   308  // "user.user u, user.user_detail ut" => "`user`.`user` u,`user`.`user_detail` ut"
   309  // "u.id, u.name, u.age"              => "`u`.`id`,`u`.`name`,`u`.`age`"
   310  // "u.id asc"                         => "`u`.`id` asc"
   311  func doQuoteString(s, charLeft, charRight string) string {
   312  	array1 := gstr.SplitAndTrim(s, ",")
   313  	for k1, v1 := range array1 {
   314  		array2 := gstr.SplitAndTrim(v1, " ")
   315  		array3 := gstr.Split(gstr.Trim(array2[0]), ".")
   316  		if len(array3) == 1 {
   317  			array3[0] = doQuoteWord(array3[0], charLeft, charRight)
   318  		} else if len(array3) >= 2 {
   319  			array3[0] = doQuoteWord(array3[0], charLeft, charRight)
   320  			// Note:
   321  			// mysql: u.uid
   322  			// mssql double dots: Database..Table
   323  			array3[len(array3)-1] = doQuoteWord(array3[len(array3)-1], charLeft, charRight)
   324  		}
   325  		array2[0] = gstr.Join(array3, ".")
   326  		array1[k1] = gstr.Join(array2, " ")
   327  	}
   328  	return gstr.Join(array1, ",")
   329  }
   330  
   331  // GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
   332  // This function automatically retrieves primary or unique field and its attribute value as condition.
   333  func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) {
   334  	tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
   335  	if err != nil {
   336  		return "", nil, err
   337  	}
   338  	array := ([]string)(nil)
   339  	for _, field := range tagField {
   340  		array = strings.Split(field.TagValue, ",")
   341  		if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) {
   342  			return array[0], []interface{}{field.Value.Interface()}, nil
   343  		}
   344  		if len(where) > 0 {
   345  			where += " AND "
   346  		}
   347  		where += field.TagValue + "=?"
   348  		args = append(args, field.Value.Interface())
   349  	}
   350  	return
   351  }
   352  
   353  // GetPrimaryKey retrieves and returns primary key field name from given struct.
   354  func GetPrimaryKey(pointer interface{}) (string, error) {
   355  	tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
   356  	if err != nil {
   357  		return "", err
   358  	}
   359  	array := ([]string)(nil)
   360  	for _, field := range tagField {
   361  		array = strings.Split(field.TagValue, ",")
   362  		if len(array) > 1 && array[1] == OrmTagForPrimary {
   363  			return array[0], nil
   364  		}
   365  	}
   366  	return "", nil
   367  }
   368  
   369  // GetPrimaryKeyCondition returns a new where condition by primary field name.
   370  // The optional parameter `where` is like follows:
   371  // 123                             => primary=123
   372  // []int{1, 2, 3}                  => primary IN(1,2,3)
   373  // "john"                          => primary='john'
   374  // []string{"john", "smith"}       => primary IN('john','smith')
   375  // g.Map{"id": g.Slice{1,2,3}}     => id IN(1,2,3)
   376  // g.Map{"id": 1, "name": "john"}  => id=1 AND name='john'
   377  // etc.
   378  //
   379  // Note that it returns the given `where` parameter directly if the `primary` is empty
   380  // or length of `where` > 1.
   381  func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
   382  	if len(where) == 0 {
   383  		return nil
   384  	}
   385  	if primary == "" {
   386  		return where
   387  	}
   388  	if len(where) == 1 {
   389  		var (
   390  			rv   = reflect.ValueOf(where[0])
   391  			kind = rv.Kind()
   392  		)
   393  		if kind == reflect.Ptr {
   394  			rv = rv.Elem()
   395  			kind = rv.Kind()
   396  		}
   397  		switch kind {
   398  		case reflect.Map, reflect.Struct:
   399  			// Ignore the parameter `primary`.
   400  			break
   401  
   402  		default:
   403  			return []interface{}{map[string]interface{}{
   404  				primary: where[0],
   405  			}}
   406  		}
   407  	}
   408  	return where
   409  }
   410  
   411  // formatSql formats the sql string and its arguments before executing.
   412  // The internal handleArguments function might be called twice during the SQL procedure,
   413  // but do not worry about it, it's safe and efficient.
   414  func formatSql(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
   415  	// DO NOT do this as there may be multiple lines and comments in the sql.
   416  	// sql = gstr.Trim(sql)
   417  	// sql = gstr.Replace(sql, "\n", " ")
   418  	// sql, _ = gregex.ReplaceString(`\s{2,}`, ` `, sql)
   419  	return handleArguments(sql, args)
   420  }
   421  
   422  type formatWhereInput struct {
   423  	Where     interface{}
   424  	Args      []interface{}
   425  	OmitNil   bool
   426  	OmitEmpty bool
   427  	Schema    string
   428  	Table     string
   429  }
   430  
   431  // formatWhere formats where statement and its arguments for `Where` and `Having` statements.
   432  func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interface{}) {
   433  	var (
   434  		buffer       = bytes.NewBuffer(nil)
   435  		reflectValue = reflect.ValueOf(in.Where)
   436  		reflectKind  = reflectValue.Kind()
   437  	)
   438  	for reflectKind == reflect.Ptr {
   439  		reflectValue = reflectValue.Elem()
   440  		reflectKind = reflectValue.Kind()
   441  	}
   442  	switch reflectKind {
   443  	case reflect.Array, reflect.Slice:
   444  		newArgs = formatWhereInterfaces(db, gconv.Interfaces(in.Where), buffer, newArgs)
   445  
   446  	case reflect.Map:
   447  		for key, value := range DataToMapDeep(in.Where) {
   448  			if gregex.IsMatchString(regularFieldNameRegPattern, key) {
   449  				if in.OmitNil && empty.IsNil(value) {
   450  					continue
   451  				}
   452  				if in.OmitEmpty && empty.IsEmpty(value) {
   453  					continue
   454  				}
   455  			}
   456  			newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
   457  		}
   458  
   459  	case reflect.Struct:
   460  		// If `where` struct implements apiIterator interface,
   461  		// it then uses its Iterate function to iterate its key-value pairs.
   462  		// For example, ListMap and TreeMap are ordered map,
   463  		// which implement apiIterator interface and are index-friendly for where conditions.
   464  		if iterator, ok := in.Where.(apiIterator); ok {
   465  			iterator.Iterator(func(key, value interface{}) bool {
   466  				ketStr := gconv.String(key)
   467  				if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) {
   468  					if in.OmitNil && empty.IsNil(value) {
   469  						return true
   470  					}
   471  					if in.OmitEmpty && empty.IsEmpty(value) {
   472  						return true
   473  					}
   474  				}
   475  				newArgs = formatWhereKeyValue(db, buffer, newArgs, ketStr, value)
   476  				return true
   477  			})
   478  			break
   479  		}
   480  		// Automatically mapping and filtering the struct attribute.
   481  		var (
   482  			reflectType = reflectValue.Type()
   483  			structField reflect.StructField
   484  		)
   485  		data := DataToMapDeep(in.Where)
   486  		if in.Table != "" {
   487  			data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true)
   488  		}
   489  		// Put the struct attributes in sequence in Where statement.
   490  		for i := 0; i < reflectType.NumField(); i++ {
   491  			structField = reflectType.Field(i)
   492  			foundKey, foundValue := gutil.MapPossibleItemByKey(data, structField.Name)
   493  			if foundKey != "" {
   494  				if in.OmitNil && empty.IsNil(foundValue) {
   495  					continue
   496  				}
   497  				if in.OmitEmpty && empty.IsEmpty(foundValue) {
   498  					continue
   499  				}
   500  				newArgs = formatWhereKeyValue(db, buffer, newArgs, foundKey, foundValue)
   501  			}
   502  		}
   503  
   504  	default:
   505  		// Usually a string.
   506  		var (
   507  			i        = 0
   508  			whereStr = gconv.String(in.Where)
   509  		)
   510  		for {
   511  			if i >= len(in.Args) {
   512  				break
   513  			}
   514  			// Sub query, which is always used along with a string condition.
   515  			if model, ok := in.Args[i].(*Model); ok {
   516  				var (
   517  					index = -1
   518  				)
   519  				whereStr, _ = gregex.ReplaceStringFunc(`(\?)`, whereStr, func(s string) string {
   520  					index++
   521  					if i+len(newArgs) == index {
   522  						sqlWithHolder, holderArgs := model.getFormattedSqlAndArgs(queryTypeNormal, false)
   523  						newArgs = append(newArgs, holderArgs...)
   524  						// Automatically adding the brackets.
   525  						return "(" + sqlWithHolder + ")"
   526  					}
   527  					return s
   528  				})
   529  				in.Args = gutil.SliceDelete(in.Args, i)
   530  				continue
   531  			}
   532  			i++
   533  		}
   534  		buffer.WriteString(whereStr)
   535  	}
   536  
   537  	if buffer.Len() == 0 {
   538  		return "", in.Args
   539  	}
   540  	newArgs = append(newArgs, in.Args...)
   541  	newWhere = buffer.String()
   542  	if len(newArgs) > 0 {
   543  		if gstr.Pos(newWhere, "?") == -1 {
   544  			if gregex.IsMatchString(lastOperatorRegPattern, newWhere) {
   545  				// Eg: Where/And/Or("uid>=", 1)
   546  				newWhere += "?"
   547  			} else if gregex.IsMatchString(regularFieldNameRegPattern, newWhere) {
   548  				newWhere = db.GetCore().QuoteString(newWhere)
   549  				if len(newArgs) > 0 {
   550  					if utils.IsArray(newArgs[0]) {
   551  						// Eg:
   552  						// Where("id", []int{1,2,3})
   553  						// Where("user.id", []int{1,2,3})
   554  						newWhere += " IN (?)"
   555  					} else if empty.IsNil(newArgs[0]) {
   556  						// Eg:
   557  						// Where("id", nil)
   558  						// Where("user.id", nil)
   559  						newWhere += " IS NULL"
   560  						newArgs = nil
   561  					} else {
   562  						// Eg:
   563  						// Where/And/Or("uid", 1)
   564  						// Where/And/Or("user.uid", 1)
   565  						newWhere += "=?"
   566  					}
   567  				}
   568  			}
   569  		}
   570  	}
   571  	return handleArguments(newWhere, newArgs)
   572  }
   573  
   574  // formatWhereInterfaces formats `where` as []interface{}.
   575  func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
   576  	if len(where) == 0 {
   577  		return newArgs
   578  	}
   579  	if len(where)%2 != 0 {
   580  		buffer.WriteString(gstr.Join(gconv.Strings(where), ""))
   581  		return newArgs
   582  	}
   583  	var str string
   584  	for i := 0; i < len(where); i += 2 {
   585  		str = gconv.String(where[i])
   586  		if buffer.Len() > 0 {
   587  			buffer.WriteString(" AND " + db.GetCore().QuoteWord(str) + "=?")
   588  		} else {
   589  			buffer.WriteString(db.GetCore().QuoteWord(str) + "=?")
   590  		}
   591  		if s, ok := where[i+1].(Raw); ok {
   592  			buffer.WriteString(gconv.String(s))
   593  		} else {
   594  			newArgs = append(newArgs, where[i+1])
   595  		}
   596  	}
   597  	return newArgs
   598  }
   599  
   600  // formatWhereKeyValue handles each key-value pair of the parameter map.
   601  func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key string, value interface{}) []interface{} {
   602  	quotedKey := db.GetCore().QuoteWord(key)
   603  	if buffer.Len() > 0 {
   604  		buffer.WriteString(" AND ")
   605  	}
   606  	// If the value is type of slice, and there's only one '?' holder in
   607  	// the key string, it automatically adds '?' holder chars according to its arguments count
   608  	// and converts it to "IN" statement.
   609  	var (
   610  		rv   = reflect.ValueOf(value)
   611  		kind = rv.Kind()
   612  	)
   613  	switch kind {
   614  	case reflect.Slice, reflect.Array:
   615  		count := gstr.Count(quotedKey, "?")
   616  		if count == 0 {
   617  			buffer.WriteString(quotedKey + " IN(?)")
   618  			newArgs = append(newArgs, value)
   619  		} else if count != rv.Len() {
   620  			buffer.WriteString(quotedKey)
   621  			newArgs = append(newArgs, value)
   622  		} else {
   623  			buffer.WriteString(quotedKey)
   624  			newArgs = append(newArgs, gconv.Interfaces(value)...)
   625  		}
   626  	default:
   627  		if value == nil || empty.IsNil(rv) {
   628  			if gregex.IsMatchString(regularFieldNameRegPattern, key) {
   629  				// The key is a single field name.
   630  				buffer.WriteString(quotedKey + " IS NULL")
   631  			} else {
   632  				// The key may have operation chars.
   633  				buffer.WriteString(quotedKey)
   634  			}
   635  		} else {
   636  			// It also supports "LIKE" statement, which we considers it an operator.
   637  			quotedKey = gstr.Trim(quotedKey)
   638  			if gstr.Pos(quotedKey, "?") == -1 {
   639  				like := " like"
   640  				if len(quotedKey) > len(like) && gstr.Equal(quotedKey[len(quotedKey)-len(like):], like) {
   641  					// Eg: Where(g.Map{"name like": "john%"})
   642  					buffer.WriteString(quotedKey + " ?")
   643  				} else if gregex.IsMatchString(lastOperatorRegPattern, quotedKey) {
   644  					// Eg: Where(g.Map{"age > ": 16})
   645  					buffer.WriteString(quotedKey + " ?")
   646  				} else if gregex.IsMatchString(regularFieldNameRegPattern, key) {
   647  					// The key is a regular field name.
   648  					buffer.WriteString(quotedKey + "=?")
   649  				} else {
   650  					// The key is not a regular field name.
   651  					// Eg: Where(g.Map{"age > 16": nil})
   652  					// Issue: https://github.com/gogf/gf/issues/765
   653  					if empty.IsEmpty(value) {
   654  						buffer.WriteString(quotedKey)
   655  						break
   656  					} else {
   657  						buffer.WriteString(quotedKey + "=?")
   658  					}
   659  				}
   660  			} else {
   661  				buffer.WriteString(quotedKey)
   662  			}
   663  			if s, ok := value.(Raw); ok {
   664  				buffer.WriteString(gconv.String(s))
   665  			} else {
   666  				newArgs = append(newArgs, value)
   667  			}
   668  		}
   669  	}
   670  	return newArgs
   671  }
   672  
   673  // handleArguments is an important function, which handles the sql and all its arguments
   674  // before committing them to underlying driver.
   675  func handleArguments(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
   676  	newSql = sql
   677  	// insertHolderCount is used to calculate the inserting position for the '?' holder.
   678  	insertHolderCount := 0
   679  	// Handles the slice arguments.
   680  	if len(args) > 0 {
   681  		for index, arg := range args {
   682  			var (
   683  				reflectValue = reflect.ValueOf(arg)
   684  				reflectKind  = reflectValue.Kind()
   685  			)
   686  			for reflectKind == reflect.Ptr {
   687  				reflectValue = reflectValue.Elem()
   688  				reflectKind = reflectValue.Kind()
   689  			}
   690  			switch reflectKind {
   691  			case reflect.Slice, reflect.Array:
   692  				// It does not split the type of []byte.
   693  				// Eg: table.Where("name = ?", []byte("john"))
   694  				if _, ok := arg.([]byte); ok {
   695  					newArgs = append(newArgs, arg)
   696  					continue
   697  				}
   698  
   699  				if reflectValue.Len() == 0 {
   700  					// Empty slice argument, it converts the sql to a false sql.
   701  					// Eg:
   702  					// Query("select * from xxx where id in(?)", g.Slice{}) -> select * from xxx where 0=1
   703  					// Where("id in(?)", g.Slice{}) -> WHERE 0=1
   704  					if gstr.Contains(newSql, "?") {
   705  						whereKeyWord := " WHERE "
   706  						if p := gstr.PosI(newSql, whereKeyWord); p == -1 {
   707  							return "0=1", []interface{}{}
   708  						} else {
   709  							return gstr.SubStr(newSql, 0, p+len(whereKeyWord)) + "0=1", []interface{}{}
   710  						}
   711  					}
   712  				} else {
   713  					for i := 0; i < reflectValue.Len(); i++ {
   714  						newArgs = append(newArgs, reflectValue.Index(i).Interface())
   715  					}
   716  				}
   717  
   718  				// If the '?' holder count equals the length of the slice,
   719  				// it does not implement the arguments splitting logic.
   720  				// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
   721  				if len(args) == 1 && gstr.Count(newSql, "?") == reflectValue.Len() {
   722  					break
   723  				}
   724  				// counter is used to finding the inserting position for the '?' holder.
   725  				var (
   726  					counter  = 0
   727  					replaced = false
   728  				)
   729  				newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string {
   730  					if replaced {
   731  						return s
   732  					}
   733  					counter++
   734  					if counter == index+insertHolderCount+1 {
   735  						replaced = true
   736  						insertHolderCount += reflectValue.Len() - 1
   737  						return "?" + strings.Repeat(",?", reflectValue.Len()-1)
   738  					}
   739  					return s
   740  				})
   741  
   742  			// Special struct handling.
   743  			case reflect.Struct:
   744  				switch v := arg.(type) {
   745  				// The underlying driver supports time.Time/*time.Time types.
   746  				case time.Time, *time.Time:
   747  					newArgs = append(newArgs, arg)
   748  					continue
   749  
   750  				// Special handling for gtime.Time/*gtime.Time.
   751  				//
   752  				// DO NOT use its underlying gtime.Time.Time as its argument,
   753  				// because the std time.Time will be converted to certain timezone
   754  				// according to underlying driver. And the underlying driver also
   755  				// converts the time.Time to string automatically as the following does.
   756  				case gtime.Time:
   757  					newArgs = append(newArgs, v.String())
   758  					continue
   759  
   760  				case *gtime.Time:
   761  					newArgs = append(newArgs, v.String())
   762  					continue
   763  
   764  				default:
   765  					// It converts the struct to string in default
   766  					// if it has implemented the String interface.
   767  					if v, ok := arg.(apiString); ok {
   768  						newArgs = append(newArgs, v.String())
   769  						continue
   770  					}
   771  				}
   772  				newArgs = append(newArgs, arg)
   773  
   774  			default:
   775  				newArgs = append(newArgs, arg)
   776  			}
   777  		}
   778  	}
   779  	return
   780  }
   781  
   782  // formatError customizes and returns the SQL error.
   783  func formatError(err error, s string, args ...interface{}) error {
   784  	if err != nil && err != sql.ErrNoRows {
   785  		return gerror.NewCodef(gcode.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args))
   786  	}
   787  	return err
   788  }
   789  
   790  // FormatSqlWithArgs binds the arguments to the sql string and returns a complete
   791  // sql string, just for debugging.
   792  func FormatSqlWithArgs(sql string, args []interface{}) string {
   793  	index := -1
   794  	newQuery, _ := gregex.ReplaceStringFunc(
   795  		`(\?|:v\d+|\$\d+|@p\d+)`,
   796  		sql,
   797  		func(s string) string {
   798  			index++
   799  			if len(args) > index {
   800  				if args[index] == nil {
   801  					return "null"
   802  				}
   803  				var (
   804  					rv   = reflect.ValueOf(args[index])
   805  					kind = rv.Kind()
   806  				)
   807  				if kind == reflect.Ptr {
   808  					if rv.IsNil() || !rv.IsValid() {
   809  						return "null"
   810  					}
   811  					rv = rv.Elem()
   812  					kind = rv.Kind()
   813  				}
   814  				switch kind {
   815  				case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
   816  					return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
   817  
   818  				case reflect.Struct:
   819  					if t, ok := args[index].(time.Time); ok {
   820  						return `'` + t.Format(`2006-01-02 15:04:05`) + `'`
   821  					}
   822  					return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
   823  				}
   824  				return gconv.String(args[index])
   825  			}
   826  			return s
   827  		})
   828  	return newQuery
   829  }