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