github.com/gogf/gf/v2@v2.7.4/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  	"context"
    12  	"fmt"
    13  	"reflect"
    14  	"regexp"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/gogf/gf/v2/container/garray"
    19  	"github.com/gogf/gf/v2/encoding/ghash"
    20  	"github.com/gogf/gf/v2/encoding/gjson"
    21  	"github.com/gogf/gf/v2/internal/empty"
    22  	"github.com/gogf/gf/v2/internal/intlog"
    23  	"github.com/gogf/gf/v2/internal/json"
    24  	"github.com/gogf/gf/v2/internal/reflection"
    25  	"github.com/gogf/gf/v2/internal/utils"
    26  	"github.com/gogf/gf/v2/os/gstructs"
    27  	"github.com/gogf/gf/v2/os/gtime"
    28  	"github.com/gogf/gf/v2/text/gregex"
    29  	"github.com/gogf/gf/v2/text/gstr"
    30  	"github.com/gogf/gf/v2/util/gconv"
    31  	"github.com/gogf/gf/v2/util/gmeta"
    32  	"github.com/gogf/gf/v2/util/gtag"
    33  	"github.com/gogf/gf/v2/util/gutil"
    34  )
    35  
    36  // iString is the type assert api for String.
    37  type iString interface {
    38  	String() string
    39  }
    40  
    41  // iIterator is the type assert api for Iterator.
    42  type iIterator interface {
    43  	Iterator(f func(key, value interface{}) bool)
    44  }
    45  
    46  // iInterfaces is the type assert api for Interfaces.
    47  type iInterfaces interface {
    48  	Interfaces() []interface{}
    49  }
    50  
    51  // iNil if the type assert api for IsNil.
    52  type iNil interface {
    53  	IsNil() bool
    54  }
    55  
    56  // iTableName is the interface for retrieving table name for struct.
    57  type iTableName interface {
    58  	TableName() string
    59  }
    60  
    61  const (
    62  	OrmTagForStruct       = "orm"
    63  	OrmTagForTable        = "table"
    64  	OrmTagForWith         = "with"
    65  	OrmTagForWithWhere    = "where"
    66  	OrmTagForWithOrder    = "order"
    67  	OrmTagForWithUnscoped = "unscoped"
    68  	OrmTagForDo           = "do"
    69  )
    70  
    71  var (
    72  	// quoteWordReg is the regular expression object for a word check.
    73  	quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
    74  
    75  	// structTagPriority tags for struct converting for orm field mapping.
    76  	structTagPriority = append([]string{OrmTagForStruct}, gtag.StructTagPriority...)
    77  )
    78  
    79  // WithDB injects given db object into context and returns a new context.
    80  func WithDB(ctx context.Context, db DB) context.Context {
    81  	if db == nil {
    82  		return ctx
    83  	}
    84  	dbCtx := db.GetCtx()
    85  	if ctxDb := DBFromCtx(dbCtx); ctxDb != nil {
    86  		return dbCtx
    87  	}
    88  	ctx = context.WithValue(ctx, ctxKeyForDB, db)
    89  	return ctx
    90  }
    91  
    92  // DBFromCtx retrieves and returns DB object from context.
    93  func DBFromCtx(ctx context.Context) DB {
    94  	if ctx == nil {
    95  		return nil
    96  	}
    97  	v := ctx.Value(ctxKeyForDB)
    98  	if v != nil {
    99  		return v.(DB)
   100  	}
   101  	return nil
   102  }
   103  
   104  // ToSQL formats and returns the last one of sql statements in given closure function
   105  // WITHOUT TRULY EXECUTING IT.
   106  // Be caution that, all the following sql statements should use the context object passing by function `f`.
   107  func ToSQL(ctx context.Context, f func(ctx context.Context) error) (sql string, err error) {
   108  	var manager = &CatchSQLManager{
   109  		SQLArray: garray.NewStrArray(),
   110  		DoCommit: false,
   111  	}
   112  	ctx = context.WithValue(ctx, ctxKeyCatchSQL, manager)
   113  	err = f(ctx)
   114  	sql, _ = manager.SQLArray.PopRight()
   115  	return
   116  }
   117  
   118  // CatchSQL catches and returns all sql statements that are EXECUTED in given closure function.
   119  // Be caution that, all the following sql statements should use the context object passing by function `f`.
   120  func CatchSQL(ctx context.Context, f func(ctx context.Context) error) (sqlArray []string, err error) {
   121  	var manager = &CatchSQLManager{
   122  		SQLArray: garray.NewStrArray(),
   123  		DoCommit: true,
   124  	}
   125  	ctx = context.WithValue(ctx, ctxKeyCatchSQL, manager)
   126  	err = f(ctx)
   127  	return manager.SQLArray.Slice(), err
   128  }
   129  
   130  // isDoStruct checks and returns whether given type is a DO struct.
   131  func isDoStruct(object interface{}) bool {
   132  	// It checks by struct name like "XxxForDao", to be compatible with old version.
   133  	// TODO remove this compatible codes in future.
   134  	reflectType := reflect.TypeOf(object)
   135  	if gstr.HasSuffix(reflectType.String(), modelForDaoSuffix) {
   136  		return true
   137  	}
   138  	// It checks by struct meta for DO struct in version.
   139  	if ormTag := gmeta.Get(object, OrmTagForStruct); !ormTag.IsEmpty() {
   140  		match, _ := gregex.MatchString(
   141  			fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForDo),
   142  			ormTag.String(),
   143  		)
   144  		if len(match) > 1 {
   145  			return gconv.Bool(match[1])
   146  		}
   147  	}
   148  	return false
   149  }
   150  
   151  // getTableNameFromOrmTag retrieves and returns the table name from struct object.
   152  func getTableNameFromOrmTag(object interface{}) string {
   153  	var tableName string
   154  	// Use the interface value.
   155  	if r, ok := object.(iTableName); ok {
   156  		tableName = r.TableName()
   157  	}
   158  	// User meta data tag "orm".
   159  	if tableName == "" {
   160  		if ormTag := gmeta.Get(object, OrmTagForStruct); !ormTag.IsEmpty() {
   161  			match, _ := gregex.MatchString(
   162  				fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForTable),
   163  				ormTag.String(),
   164  			)
   165  			if len(match) > 1 {
   166  				tableName = match[1]
   167  			}
   168  		}
   169  	}
   170  	// Use the struct name of snake case.
   171  	if tableName == "" {
   172  		if t, err := gstructs.StructType(object); err != nil {
   173  			panic(err)
   174  		} else {
   175  			tableName = gstr.CaseSnakeFirstUpper(
   176  				gstr.StrEx(t.String(), "."),
   177  			)
   178  		}
   179  	}
   180  	return tableName
   181  }
   182  
   183  // ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
   184  // Note that the parameter `list` should be type of slice which contains elements of map or struct,
   185  // or else it returns an empty slice.
   186  //
   187  // The parameter `list` supports types like:
   188  // []map[string]interface{}
   189  // []map[string]sub-map
   190  // []struct
   191  // []struct:sub-struct
   192  // Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given.
   193  // See gutil.ListItemValues.
   194  func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) {
   195  	return gutil.ListItemValues(list, key, subKey...)
   196  }
   197  
   198  // ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
   199  // Note that the parameter `list` should be type of slice which contains elements of map or struct,
   200  // or else it returns an empty slice.
   201  // See gutil.ListItemValuesUnique.
   202  func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} {
   203  	return gutil.ListItemValuesUnique(list, key, subKey...)
   204  }
   205  
   206  // GetInsertOperationByOption returns proper insert option with given parameter `option`.
   207  func GetInsertOperationByOption(option InsertOption) string {
   208  	var operator string
   209  	switch option {
   210  	case InsertOptionReplace:
   211  		operator = InsertOperationReplace
   212  	case InsertOptionIgnore:
   213  		operator = InsertOperationIgnore
   214  	default:
   215  		operator = InsertOperationInsert
   216  	}
   217  	return operator
   218  }
   219  
   220  func anyValueToMapBeforeToRecord(value interface{}) map[string]interface{} {
   221  	convertedMap := gconv.Map(value, gconv.MapOption{
   222  		Tags:      structTagPriority,
   223  		OmitEmpty: true, // To be compatible with old version from v2.6.0.
   224  	})
   225  	if gutil.OriginValueAndKind(value).OriginKind != reflect.Struct {
   226  		return convertedMap
   227  	}
   228  	// It here converts all struct/map slice attributes to json string.
   229  	for k, v := range convertedMap {
   230  		originValueAndKind := gutil.OriginValueAndKind(v)
   231  		switch originValueAndKind.OriginKind {
   232  		// Check map item slice item.
   233  		case reflect.Array, reflect.Slice:
   234  			mapItemValue := originValueAndKind.OriginValue
   235  			if mapItemValue.Len() == 0 {
   236  				break
   237  			}
   238  			// Check slice item type struct/map type.
   239  			switch mapItemValue.Index(0).Kind() {
   240  			case reflect.Struct, reflect.Map:
   241  				mapItemJsonBytes, err := json.Marshal(v)
   242  				if err != nil {
   243  					// Do not eat any error.
   244  					intlog.Error(context.TODO(), err)
   245  				}
   246  				convertedMap[k] = mapItemJsonBytes
   247  			}
   248  		}
   249  	}
   250  	return convertedMap
   251  }
   252  
   253  // MapOrStructToMapDeep converts `value` to map type recursively(if attribute struct is embedded).
   254  // The parameter `value` should be type of *map/map/*struct/struct.
   255  // It supports embedded struct definition for struct.
   256  func MapOrStructToMapDeep(value interface{}, omitempty bool) map[string]interface{} {
   257  	m := gconv.Map(value, gconv.MapOption{
   258  		Tags:      structTagPriority,
   259  		OmitEmpty: omitempty,
   260  	})
   261  	for k, v := range m {
   262  		switch v.(type) {
   263  		case time.Time, *time.Time, gtime.Time, *gtime.Time, gjson.Json, *gjson.Json:
   264  			m[k] = v
   265  		}
   266  	}
   267  	return m
   268  }
   269  
   270  // doQuoteTableName adds prefix string and quote chars for table name. It handles table string like:
   271  // "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut",
   272  // "user.user u", "`user`.`user` u".
   273  //
   274  // Note that, this will automatically check the table prefix whether already added, if true it does
   275  // nothing to the table name, or else adds the prefix to the table name and returns new table name with prefix.
   276  func doQuoteTableName(table, prefix, charLeft, charRight string) string {
   277  	var (
   278  		index  int
   279  		chars  = charLeft + charRight
   280  		array1 = gstr.SplitAndTrim(table, ",")
   281  	)
   282  	for k1, v1 := range array1 {
   283  		array2 := gstr.SplitAndTrim(v1, " ")
   284  		// Trim the security chars.
   285  		array2[0] = gstr.Trim(array2[0], chars)
   286  		// Check whether it has database name.
   287  		array3 := gstr.Split(gstr.Trim(array2[0]), ".")
   288  		for k, v := range array3 {
   289  			array3[k] = gstr.Trim(v, chars)
   290  		}
   291  		index = len(array3) - 1
   292  		// If the table name already has the prefix, skips the prefix adding.
   293  		if len(array3[index]) <= len(prefix) || array3[index][:len(prefix)] != prefix {
   294  			array3[index] = prefix + array3[index]
   295  		}
   296  		array2[0] = gstr.Join(array3, ".")
   297  		// Add the security chars.
   298  		array2[0] = doQuoteString(array2[0], charLeft, charRight)
   299  		array1[k1] = gstr.Join(array2, " ")
   300  	}
   301  	return gstr.Join(array1, ",")
   302  }
   303  
   304  // doQuoteWord checks given string `s` a word, if true quotes it with `charLeft` and `charRight`
   305  // and returns the quoted string; or else returns `s` without any change.
   306  func doQuoteWord(s, charLeft, charRight string) string {
   307  	if quoteWordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
   308  		return charLeft + s + charRight
   309  	}
   310  	return s
   311  }
   312  
   313  // doQuoteString quotes string with quote chars.
   314  // For example, if quote char is '`':
   315  // "null"                             => "NULL"
   316  // "user"                             => "`user`"
   317  // "user u"                           => "`user` u"
   318  // "user,user_detail"                 => "`user`,`user_detail`"
   319  // "user u, user_detail ut"           => "`user` u,`user_detail` ut"
   320  // "user.user u, user.user_detail ut" => "`user`.`user` u,`user`.`user_detail` ut"
   321  // "u.id, u.name, u.age"              => "`u`.`id`,`u`.`name`,`u`.`age`"
   322  // "u.id asc"                         => "`u`.`id` asc".
   323  func doQuoteString(s, charLeft, charRight string) string {
   324  	array1 := gstr.SplitAndTrim(s, ",")
   325  	for k1, v1 := range array1 {
   326  		array2 := gstr.SplitAndTrim(v1, " ")
   327  		array3 := gstr.Split(gstr.Trim(array2[0]), ".")
   328  		if len(array3) == 1 {
   329  			if strings.EqualFold(array3[0], "NULL") {
   330  				array3[0] = doQuoteWord(array3[0], "", "")
   331  			} else {
   332  				array3[0] = doQuoteWord(array3[0], charLeft, charRight)
   333  			}
   334  		} else if len(array3) >= 2 {
   335  			array3[0] = doQuoteWord(array3[0], charLeft, charRight)
   336  			// Note:
   337  			// mysql: u.uid
   338  			// mssql double dots: Database..Table
   339  			array3[len(array3)-1] = doQuoteWord(array3[len(array3)-1], charLeft, charRight)
   340  		}
   341  		array2[0] = gstr.Join(array3, ".")
   342  		array1[k1] = gstr.Join(array2, " ")
   343  	}
   344  	return gstr.Join(array1, ",")
   345  }
   346  
   347  func getFieldsFromStructOrMap(structOrMap interface{}) (fields []string) {
   348  	fields = []string{}
   349  	if utils.IsStruct(structOrMap) {
   350  		structFields, _ := gstructs.Fields(gstructs.FieldsInput{
   351  			Pointer:         structOrMap,
   352  			RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
   353  		})
   354  		var ormTagValue string
   355  		for _, structField := range structFields {
   356  			ormTagValue = structField.Tag(OrmTagForStruct)
   357  			ormTagValue = gstr.Split(gstr.Trim(ormTagValue), ",")[0]
   358  			if ormTagValue != "" && gregex.IsMatchString(regularFieldNameRegPattern, ormTagValue) {
   359  				fields = append(fields, ormTagValue)
   360  			} else {
   361  				fields = append(fields, structField.Name())
   362  			}
   363  		}
   364  	} else {
   365  		fields = gutil.Keys(structOrMap)
   366  	}
   367  	return
   368  }
   369  
   370  // GetPrimaryKeyCondition returns a new where condition by primary field name.
   371  // The optional parameter `where` is like follows:
   372  // 123                             => primary=123
   373  // []int{1, 2, 3}                  => primary IN(1,2,3)
   374  // "john"                          => primary='john'
   375  // []string{"john", "smith"}       => primary IN('john','smith')
   376  // g.Map{"id": g.Slice{1,2,3}}     => id IN(1,2,3)
   377  // g.Map{"id": 1, "name": "john"}  => id=1 AND name='john'
   378  // etc.
   379  //
   380  // Note that it returns the given `where` parameter directly if the `primary` is empty
   381  // or length of `where` > 1.
   382  func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
   383  	if len(where) == 0 {
   384  		return nil
   385  	}
   386  	if primary == "" {
   387  		return where
   388  	}
   389  	if len(where) == 1 {
   390  		var (
   391  			rv   = reflect.ValueOf(where[0])
   392  			kind = rv.Kind()
   393  		)
   394  		if kind == reflect.Ptr {
   395  			rv = rv.Elem()
   396  			kind = rv.Kind()
   397  		}
   398  		switch kind {
   399  		case reflect.Map, reflect.Struct:
   400  			// Ignore the parameter `primary`.
   401  			break
   402  
   403  		default:
   404  			return []interface{}{map[string]interface{}{
   405  				primary: where[0],
   406  			}}
   407  		}
   408  	}
   409  	return where
   410  }
   411  
   412  type formatWhereHolderInput struct {
   413  	WhereHolder
   414  	OmitNil   bool
   415  	OmitEmpty bool
   416  	Schema    string
   417  	Table     string // Table is used for fields mapping and filtering internally.
   418  }
   419  
   420  func isKeyValueCanBeOmitEmpty(omitEmpty bool, whereType string, key, value interface{}) bool {
   421  	if !omitEmpty {
   422  		return false
   423  	}
   424  	// Eg:
   425  	// Where("id", []int{}).All()             -> SELECT xxx FROM xxx WHERE 0=1
   426  	// Where("name", "").All()                -> SELECT xxx FROM xxx WHERE `name`=''
   427  	// OmitEmpty().Where("id", []int{}).All() -> SELECT xxx FROM xxx
   428  	// OmitEmpty().Where("name", "").All()    -> SELECT xxx FROM xxx
   429  	// OmitEmpty().Where("1").All()           -> SELECT xxx FROM xxx WHERE 1
   430  	switch whereType {
   431  	case whereHolderTypeNoArgs:
   432  		return false
   433  
   434  	case whereHolderTypeIn:
   435  		return gutil.IsEmpty(value)
   436  
   437  	default:
   438  		if gstr.Count(gconv.String(key), "?") == 0 && gutil.IsEmpty(value) {
   439  			return true
   440  		}
   441  	}
   442  	return false
   443  }
   444  
   445  // formatWhereHolder formats where statement and its arguments for `Where` and `Having` statements.
   446  func formatWhereHolder(ctx context.Context, db DB, in formatWhereHolderInput) (newWhere string, newArgs []interface{}) {
   447  	var (
   448  		buffer      = bytes.NewBuffer(nil)
   449  		reflectInfo = reflection.OriginValueAndKind(in.Where)
   450  	)
   451  	switch reflectInfo.OriginKind {
   452  	case reflect.Array, reflect.Slice:
   453  		newArgs = formatWhereInterfaces(db, gconv.Interfaces(in.Where), buffer, newArgs)
   454  
   455  	case reflect.Map:
   456  		for key, value := range MapOrStructToMapDeep(in.Where, true) {
   457  			if in.OmitNil && empty.IsNil(value) {
   458  				continue
   459  			}
   460  			if in.OmitEmpty && empty.IsEmpty(value) {
   461  				continue
   462  			}
   463  			newArgs = formatWhereKeyValue(formatWhereKeyValueInput{
   464  				Db:     db,
   465  				Buffer: buffer,
   466  				Args:   newArgs,
   467  				Key:    key,
   468  				Value:  value,
   469  				Prefix: in.Prefix,
   470  				Type:   in.Type,
   471  			})
   472  		}
   473  
   474  	case reflect.Struct:
   475  		// If the `where` parameter is `DO` struct, it then adds `OmitNil` option for this condition,
   476  		// which will filter all nil parameters in `where`.
   477  		if isDoStruct(in.Where) {
   478  			in.OmitNil = true
   479  		}
   480  		// If `where` struct implements `iIterator` interface,
   481  		// it then uses its Iterate function to iterate its key-value pairs.
   482  		// For example, ListMap and TreeMap are ordered map,
   483  		// which implement `iIterator` interface and are index-friendly for where conditions.
   484  		if iterator, ok := in.Where.(iIterator); ok {
   485  			iterator.Iterator(func(key, value interface{}) bool {
   486  				ketStr := gconv.String(key)
   487  				if in.OmitNil && empty.IsNil(value) {
   488  					return true
   489  				}
   490  				if in.OmitEmpty && empty.IsEmpty(value) {
   491  					return true
   492  				}
   493  				newArgs = formatWhereKeyValue(formatWhereKeyValueInput{
   494  					Db:        db,
   495  					Buffer:    buffer,
   496  					Args:      newArgs,
   497  					Key:       ketStr,
   498  					Value:     value,
   499  					OmitEmpty: in.OmitEmpty,
   500  					Prefix:    in.Prefix,
   501  					Type:      in.Type,
   502  				})
   503  				return true
   504  			})
   505  			break
   506  		}
   507  		// Automatically mapping and filtering the struct attribute.
   508  		var (
   509  			reflectType = reflectInfo.OriginValue.Type()
   510  			structField reflect.StructField
   511  			data        = MapOrStructToMapDeep(in.Where, true)
   512  		)
   513  		// If `Prefix` is given, it checks and retrieves the table name.
   514  		if in.Prefix != "" {
   515  			hasTable, _ := db.GetCore().HasTable(in.Prefix)
   516  			if hasTable {
   517  				in.Table = in.Prefix
   518  			} else {
   519  				ormTagTableName := getTableNameFromOrmTag(in.Where)
   520  				if ormTagTableName != "" {
   521  					in.Table = ormTagTableName
   522  				}
   523  			}
   524  		}
   525  		// Mapping and filtering fields if `Table` is given.
   526  		if in.Table != "" {
   527  			data, _ = db.GetCore().mappingAndFilterData(ctx, in.Schema, in.Table, data, true)
   528  		}
   529  		// Put the struct attributes in sequence in Where statement.
   530  		var ormTagValue string
   531  		for i := 0; i < reflectType.NumField(); i++ {
   532  			structField = reflectType.Field(i)
   533  			// Use tag value from `orm` as field name if specified.
   534  			ormTagValue = structField.Tag.Get(OrmTagForStruct)
   535  			ormTagValue = gstr.Split(gstr.Trim(ormTagValue), ",")[0]
   536  			if ormTagValue == "" {
   537  				ormTagValue = structField.Name
   538  			}
   539  			foundKey, foundValue := gutil.MapPossibleItemByKey(data, ormTagValue)
   540  			if foundKey != "" {
   541  				if in.OmitNil && empty.IsNil(foundValue) {
   542  					continue
   543  				}
   544  				if in.OmitEmpty && empty.IsEmpty(foundValue) {
   545  					continue
   546  				}
   547  				newArgs = formatWhereKeyValue(formatWhereKeyValueInput{
   548  					Db:        db,
   549  					Buffer:    buffer,
   550  					Args:      newArgs,
   551  					Key:       foundKey,
   552  					Value:     foundValue,
   553  					OmitEmpty: in.OmitEmpty,
   554  					Prefix:    in.Prefix,
   555  					Type:      in.Type,
   556  				})
   557  			}
   558  		}
   559  
   560  	default:
   561  		// Where filter.
   562  		var omitEmptyCheckValue interface{}
   563  		if len(in.Args) == 1 {
   564  			omitEmptyCheckValue = in.Args[0]
   565  		} else {
   566  			omitEmptyCheckValue = in.Args
   567  		}
   568  		if isKeyValueCanBeOmitEmpty(in.OmitEmpty, in.Type, in.Where, omitEmptyCheckValue) {
   569  			return
   570  		}
   571  		// Usually a string.
   572  		whereStr := gstr.Trim(gconv.String(in.Where))
   573  		// Is `whereStr` a field name which composed as a key-value condition?
   574  		// Eg:
   575  		// Where("id", 1)
   576  		// Where("id", g.Slice{1,2,3})
   577  		if gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, whereStr) && len(in.Args) == 1 {
   578  			newArgs = formatWhereKeyValue(formatWhereKeyValueInput{
   579  				Db:        db,
   580  				Buffer:    buffer,
   581  				Args:      newArgs,
   582  				Key:       whereStr,
   583  				Value:     in.Args[0],
   584  				OmitEmpty: in.OmitEmpty,
   585  				Prefix:    in.Prefix,
   586  				Type:      in.Type,
   587  			})
   588  			in.Args = in.Args[:0]
   589  			break
   590  		}
   591  		// If the first part is column name, it automatically adds prefix to the column.
   592  		if in.Prefix != "" {
   593  			array := gstr.Split(whereStr, " ")
   594  			if ok, _ := db.GetCore().HasField(ctx, in.Table, array[0]); ok {
   595  				whereStr = in.Prefix + "." + whereStr
   596  			}
   597  		}
   598  		// Regular string and parameter place holder handling.
   599  		// Eg:
   600  		// Where("id in(?) and name=?", g.Slice{1,2,3}, "john")
   601  		i := 0
   602  		for {
   603  			if i >= len(in.Args) {
   604  				break
   605  			}
   606  			// ===============================================================
   607  			// Sub query, which is always used along with a string condition.
   608  			// ===============================================================
   609  			if subModel, ok := in.Args[i].(*Model); ok {
   610  				index := -1
   611  				whereStr, _ = gregex.ReplaceStringFunc(`(\?)`, whereStr, func(s string) string {
   612  					index++
   613  					if i+len(newArgs) == index {
   614  						sqlWithHolder, holderArgs := subModel.getHolderAndArgsAsSubModel(ctx)
   615  						in.Args = gutil.SliceInsertAfter(in.Args, i, holderArgs...)
   616  						// Automatically adding the brackets.
   617  						return "(" + sqlWithHolder + ")"
   618  					}
   619  					return s
   620  				})
   621  				in.Args = gutil.SliceDelete(in.Args, i)
   622  				continue
   623  			}
   624  			i++
   625  		}
   626  		buffer.WriteString(whereStr)
   627  	}
   628  
   629  	if buffer.Len() == 0 {
   630  		return "", in.Args
   631  	}
   632  	if len(in.Args) > 0 {
   633  		newArgs = append(newArgs, in.Args...)
   634  	}
   635  	newWhere = buffer.String()
   636  	if len(newArgs) > 0 {
   637  		if gstr.Pos(newWhere, "?") == -1 {
   638  			if gregex.IsMatchString(lastOperatorRegPattern, newWhere) {
   639  				// Eg: Where/And/Or("uid>=", 1)
   640  				newWhere += "?"
   641  			} else if gregex.IsMatchString(regularFieldNameRegPattern, newWhere) {
   642  				newWhere = db.GetCore().QuoteString(newWhere)
   643  				if len(newArgs) > 0 {
   644  					if utils.IsArray(newArgs[0]) {
   645  						// Eg:
   646  						// Where("id", []int{1,2,3})
   647  						// Where("user.id", []int{1,2,3})
   648  						newWhere += " IN (?)"
   649  					} else if empty.IsNil(newArgs[0]) {
   650  						// Eg:
   651  						// Where("id", nil)
   652  						// Where("user.id", nil)
   653  						newWhere += " IS NULL"
   654  						newArgs = nil
   655  					} else {
   656  						// Eg:
   657  						// Where/And/Or("uid", 1)
   658  						// Where/And/Or("user.uid", 1)
   659  						newWhere += "=?"
   660  					}
   661  				}
   662  			}
   663  		}
   664  	}
   665  	return handleSliceAndStructArgsForSql(newWhere, newArgs)
   666  }
   667  
   668  // formatWhereInterfaces formats `where` as []interface{}.
   669  func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
   670  	if len(where) == 0 {
   671  		return newArgs
   672  	}
   673  	if len(where)%2 != 0 {
   674  		buffer.WriteString(gstr.Join(gconv.Strings(where), ""))
   675  		return newArgs
   676  	}
   677  	var str string
   678  	for i := 0; i < len(where); i += 2 {
   679  		str = gconv.String(where[i])
   680  		if buffer.Len() > 0 {
   681  			buffer.WriteString(" AND " + db.GetCore().QuoteWord(str) + "=?")
   682  		} else {
   683  			buffer.WriteString(db.GetCore().QuoteWord(str) + "=?")
   684  		}
   685  		if s, ok := where[i+1].(Raw); ok {
   686  			buffer.WriteString(gconv.String(s))
   687  		} else {
   688  			newArgs = append(newArgs, where[i+1])
   689  		}
   690  	}
   691  	return newArgs
   692  }
   693  
   694  type formatWhereKeyValueInput struct {
   695  	Db        DB            // Db is the underlying DB object for current operation.
   696  	Buffer    *bytes.Buffer // Buffer is the sql statement string without Args for current operation.
   697  	Args      []interface{} // Args is the full arguments of current operation.
   698  	Key       string        // The field name, eg: "id", "name", etc.
   699  	Value     interface{}   // The field value, can be any types.
   700  	Type      string        // The value in Where type.
   701  	OmitEmpty bool          // Ignores current condition key if `value` is empty.
   702  	Prefix    string        // Field prefix, eg: "user", "order", etc.
   703  }
   704  
   705  // formatWhereKeyValue handles each key-value pair of the parameter map.
   706  func formatWhereKeyValue(in formatWhereKeyValueInput) (newArgs []interface{}) {
   707  	var (
   708  		quotedKey   = in.Db.GetCore().QuoteWord(in.Key)
   709  		holderCount = gstr.Count(quotedKey, "?")
   710  	)
   711  	if isKeyValueCanBeOmitEmpty(in.OmitEmpty, in.Type, quotedKey, in.Value) {
   712  		return in.Args
   713  	}
   714  	if in.Prefix != "" && !gstr.Contains(quotedKey, ".") {
   715  		quotedKey = in.Prefix + "." + quotedKey
   716  	}
   717  	if in.Buffer.Len() > 0 {
   718  		in.Buffer.WriteString(" AND ")
   719  	}
   720  	// If the value is type of slice, and there's only one '?' holder in
   721  	// the key string, it automatically adds '?' holder chars according to its arguments count
   722  	// and converts it to "IN" statement.
   723  	var (
   724  		reflectValue = reflect.ValueOf(in.Value)
   725  		reflectKind  = reflectValue.Kind()
   726  	)
   727  	switch reflectKind {
   728  	// Slice argument.
   729  	case reflect.Slice, reflect.Array:
   730  		if holderCount == 0 {
   731  			in.Buffer.WriteString(quotedKey + " IN(?)")
   732  			in.Args = append(in.Args, in.Value)
   733  		} else {
   734  			if holderCount != reflectValue.Len() {
   735  				in.Buffer.WriteString(quotedKey)
   736  				in.Args = append(in.Args, in.Value)
   737  			} else {
   738  				in.Buffer.WriteString(quotedKey)
   739  				in.Args = append(in.Args, gconv.Interfaces(in.Value)...)
   740  			}
   741  		}
   742  
   743  	default:
   744  		if in.Value == nil || empty.IsNil(reflectValue) {
   745  			if gregex.IsMatchString(regularFieldNameRegPattern, in.Key) {
   746  				// The key is a single field name.
   747  				in.Buffer.WriteString(quotedKey + " IS NULL")
   748  			} else {
   749  				// The key may have operation chars.
   750  				in.Buffer.WriteString(quotedKey)
   751  			}
   752  		} else {
   753  			// It also supports "LIKE" statement, which we consider it an operator.
   754  			quotedKey = gstr.Trim(quotedKey)
   755  			if gstr.Pos(quotedKey, "?") == -1 {
   756  				like := " LIKE"
   757  				if len(quotedKey) > len(like) && gstr.Equal(quotedKey[len(quotedKey)-len(like):], like) {
   758  					// Eg: Where(g.Map{"name like": "john%"})
   759  					in.Buffer.WriteString(quotedKey + " ?")
   760  				} else if gregex.IsMatchString(lastOperatorRegPattern, quotedKey) {
   761  					// Eg: Where(g.Map{"age > ": 16})
   762  					in.Buffer.WriteString(quotedKey + " ?")
   763  				} else if gregex.IsMatchString(regularFieldNameRegPattern, in.Key) {
   764  					// The key is a regular field name.
   765  					in.Buffer.WriteString(quotedKey + "=?")
   766  				} else {
   767  					// The key is not a regular field name.
   768  					// Eg: Where(g.Map{"age > 16": nil})
   769  					// Issue: https://github.com/gogf/gf/issues/765
   770  					if empty.IsEmpty(in.Value) {
   771  						in.Buffer.WriteString(quotedKey)
   772  						break
   773  					} else {
   774  						in.Buffer.WriteString(quotedKey + "=?")
   775  					}
   776  				}
   777  			} else {
   778  				in.Buffer.WriteString(quotedKey)
   779  			}
   780  			in.Args = append(in.Args, in.Value)
   781  		}
   782  	}
   783  	return in.Args
   784  }
   785  
   786  // handleSliceAndStructArgsForSql is an important function, which handles the sql and all its arguments
   787  // before committing them to underlying driver.
   788  func handleSliceAndStructArgsForSql(
   789  	oldSql string, oldArgs []interface{},
   790  ) (newSql string, newArgs []interface{}) {
   791  	newSql = oldSql
   792  	if len(oldArgs) == 0 {
   793  		return
   794  	}
   795  	// insertHolderCount is used to calculate the inserting position for the '?' holder.
   796  	insertHolderCount := 0
   797  	// Handles the slice and struct type argument item.
   798  	for index, oldArg := range oldArgs {
   799  		argReflectInfo := reflection.OriginValueAndKind(oldArg)
   800  		switch argReflectInfo.OriginKind {
   801  		case reflect.Slice, reflect.Array:
   802  			// It does not split the type of []byte.
   803  			// Eg: table.Where("name = ?", []byte("john"))
   804  			if _, ok := oldArg.([]byte); ok {
   805  				newArgs = append(newArgs, oldArg)
   806  				continue
   807  			}
   808  			var (
   809  				valueHolderCount = gstr.Count(newSql, "?")
   810  				argSliceLength   = argReflectInfo.OriginValue.Len()
   811  			)
   812  			if argSliceLength == 0 {
   813  				// Empty slice argument, it converts the sql to a false sql.
   814  				// Example:
   815  				// Query("select * from xxx where id in(?)", g.Slice{}) -> select * from xxx where 0=1
   816  				// Where("id in(?)", g.Slice{}) -> WHERE 0=1
   817  				if gstr.Contains(newSql, "?") {
   818  					whereKeyWord := " WHERE "
   819  					if p := gstr.PosI(newSql, whereKeyWord); p == -1 {
   820  						return "0=1", []interface{}{}
   821  					} else {
   822  						return gstr.SubStr(newSql, 0, p+len(whereKeyWord)) + "0=1", []interface{}{}
   823  					}
   824  				}
   825  			} else {
   826  				// Example:
   827  				// Query("SELECT ?+?", g.Slice{1,2})
   828  				// WHERE("id=?", g.Slice{1,2})
   829  				for i := 0; i < argSliceLength; i++ {
   830  					newArgs = append(newArgs, argReflectInfo.OriginValue.Index(i).Interface())
   831  				}
   832  			}
   833  
   834  			// If the '?' holder count equals the length of the slice,
   835  			// it does not implement the arguments splitting logic.
   836  			// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
   837  			if len(oldArgs) == 1 && valueHolderCount == argSliceLength {
   838  				break
   839  			}
   840  
   841  			// counter is used to finding the inserting position for the '?' holder.
   842  			var (
   843  				counter  = 0
   844  				replaced = false
   845  			)
   846  			newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string {
   847  				if replaced {
   848  					return s
   849  				}
   850  				counter++
   851  				if counter == index+insertHolderCount+1 {
   852  					replaced = true
   853  					insertHolderCount += argSliceLength - 1
   854  					return "?" + strings.Repeat(",?", argSliceLength-1)
   855  				}
   856  				return s
   857  			})
   858  
   859  		// Special struct handling.
   860  		case reflect.Struct:
   861  			switch oldArg.(type) {
   862  			// The underlying driver supports time.Time/*time.Time types.
   863  			case time.Time, *time.Time:
   864  				newArgs = append(newArgs, oldArg)
   865  				continue
   866  
   867  			case gtime.Time:
   868  				newArgs = append(newArgs, oldArg.(gtime.Time).Time)
   869  				continue
   870  
   871  			case *gtime.Time:
   872  				newArgs = append(newArgs, oldArg.(*gtime.Time).Time)
   873  				continue
   874  
   875  			default:
   876  				// It converts the struct to string in default
   877  				// if it has implemented the String interface.
   878  				if v, ok := oldArg.(iString); ok {
   879  					newArgs = append(newArgs, v.String())
   880  					continue
   881  				}
   882  			}
   883  			newArgs = append(newArgs, oldArg)
   884  
   885  		default:
   886  			newArgs = append(newArgs, oldArg)
   887  		}
   888  	}
   889  	return
   890  }
   891  
   892  // FormatSqlWithArgs binds the arguments to the sql string and returns a complete
   893  // sql string, just for debugging.
   894  func FormatSqlWithArgs(sql string, args []interface{}) string {
   895  	index := -1
   896  	newQuery, _ := gregex.ReplaceStringFunc(
   897  		`(\?|:v\d+|\$\d+|@p\d+)`,
   898  		sql,
   899  		func(s string) string {
   900  			index++
   901  			if len(args) > index {
   902  				if args[index] == nil {
   903  					return "null"
   904  				}
   905  				// Parameters of type Raw do not require special treatment
   906  				if v, ok := args[index].(Raw); ok {
   907  					return gconv.String(v)
   908  				}
   909  				reflectInfo := reflection.OriginValueAndKind(args[index])
   910  				if reflectInfo.OriginKind == reflect.Ptr &&
   911  					(reflectInfo.OriginValue.IsNil() || !reflectInfo.OriginValue.IsValid()) {
   912  					return "null"
   913  				}
   914  				switch reflectInfo.OriginKind {
   915  				case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
   916  					return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
   917  
   918  				case reflect.Struct:
   919  					if t, ok := args[index].(time.Time); ok {
   920  						return `'` + t.Format(`2006-01-02 15:04:05`) + `'`
   921  					}
   922  					return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
   923  				}
   924  				return gconv.String(args[index])
   925  			}
   926  			return s
   927  		})
   928  	return newQuery
   929  }
   930  
   931  // FormatMultiLineSqlToSingle formats sql template string into one line.
   932  func FormatMultiLineSqlToSingle(sql string) (string, error) {
   933  	var err error
   934  	// format sql template string.
   935  	sql, err = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(sql))
   936  	if err != nil {
   937  		return "", err
   938  	}
   939  	sql, err = gregex.ReplaceString(`\s{2,}`, " ", gstr.Trim(sql))
   940  	if err != nil {
   941  		return "", err
   942  	}
   943  	return sql, nil
   944  }
   945  
   946  func genTableFieldsCacheKey(group, schema, table string) string {
   947  	return fmt.Sprintf(
   948  		`%s%s@%s#%s`,
   949  		cachePrefixTableFields,
   950  		group,
   951  		schema,
   952  		table,
   953  	)
   954  }
   955  
   956  func genSelectCacheKey(table, group, schema, name, sql string, args ...interface{}) string {
   957  	if name == "" {
   958  		name = fmt.Sprintf(
   959  			`%s@%s#%s:%d`,
   960  			table,
   961  			group,
   962  			schema,
   963  			ghash.BKDR64([]byte(sql+", @PARAMS:"+gconv.String(args))),
   964  		)
   965  	}
   966  	return fmt.Sprintf(`%s%s`, cachePrefixSelectCache, name)
   967  }