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 }