mirror of
https://gitee.com/johng/gf.git
synced 2024-12-12 12:45:08 +08:00
fix(database/gdb): move Raw
parameter from args to sql statement before committed to db driver (#3997)
This commit is contained in:
parent
2d0cd7b770
commit
532e665841
@ -1409,3 +1409,51 @@ func Test_Issue3968(t *testing.T) {
|
||||
t.Assert(len(result), 10)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/3915
|
||||
func Test_Issue3915(t *testing.T) {
|
||||
table := "issue3915"
|
||||
array := gstr.SplitAndTrim(gtest.DataContent(`issue3915.sql`), ";")
|
||||
for _, v := range array {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//db.SetDebug(true)
|
||||
all, err := db.Model(table).Where("a < b").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 1)
|
||||
|
||||
all, err = db.Model(table).Where(gdb.Raw("a < b")).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 1)
|
||||
|
||||
all, err = db.Model(table).WhereLT("a", gdb.Raw("`b`")).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
//db.SetDebug(true)
|
||||
all, err := db.Model(table).Where("a > b").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 2)
|
||||
|
||||
all, err = db.Model(table).Where(gdb.Raw("a > b")).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 2)
|
||||
|
||||
all, err = db.Model(table).WhereGT("a", gdb.Raw("`b`")).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 2)
|
||||
})
|
||||
}
|
||||
|
9
contrib/drivers/mysql/testdata/issue3915.sql
vendored
Normal file
9
contrib/drivers/mysql/testdata/issue3915.sql
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE `issue3915` (
|
||||
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'user id',
|
||||
`a` float DEFAULT NULL COMMENT 'user name',
|
||||
`b` float DEFAULT NULL COMMENT 'user status',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `issue3915` (`id`,`a`,`b`) VALUES (1,1,2);
|
||||
INSERT INTO `issue3915` (`id`,`a`,`b`) VALUES (2,5,4);
|
@ -789,9 +789,5 @@ func (c *Core) IsSoftCreatedFieldName(fieldName string) bool {
|
||||
// The internal handleArguments function might be called twice during the SQL procedure,
|
||||
// but do not worry about it, it's safe and efficient.
|
||||
func (c *Core) FormatSqlBeforeExecuting(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
|
||||
// DO NOT do this as there may be multiple lines and comments in the sql.
|
||||
// sql = gstr.Trim(sql)
|
||||
// sql = gstr.Replace(sql, "\n", " ")
|
||||
// sql, _ = gregex.ReplaceString(`\s{2,}`, ` `, sql)
|
||||
return handleSliceAndStructArgsForSql(sql, args)
|
||||
}
|
||||
|
@ -608,7 +608,7 @@ func formatWhereHolder(ctx context.Context, db DB, in formatWhereHolderInput) (n
|
||||
// ===============================================================
|
||||
if subModel, ok := in.Args[i].(*Model); ok {
|
||||
index := -1
|
||||
whereStr, _ = gregex.ReplaceStringFunc(`(\?)`, whereStr, func(s string) string {
|
||||
whereStr = gstr.ReplaceFunc(whereStr, `?`, func(s string) string {
|
||||
index++
|
||||
if i+len(newArgs) == index {
|
||||
sqlWithHolder, holderArgs := subModel.getHolderAndArgsAsSubModel(ctx)
|
||||
@ -843,7 +843,7 @@ func handleSliceAndStructArgsForSql(
|
||||
counter = 0
|
||||
replaced = false
|
||||
)
|
||||
newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string {
|
||||
newSql = gstr.ReplaceFunc(newSql, `?`, func(s string) string {
|
||||
if replaced {
|
||||
return s
|
||||
}
|
||||
@ -856,9 +856,20 @@ func handleSliceAndStructArgsForSql(
|
||||
return s
|
||||
})
|
||||
|
||||
// Special struct handling.
|
||||
case reflect.Struct:
|
||||
default:
|
||||
switch oldArg.(type) {
|
||||
// Do not append Raw arg to args but directly into the sql.
|
||||
case Raw, *Raw:
|
||||
var counter = 0
|
||||
newSql = gstr.ReplaceFunc(newSql, `?`, func(s string) string {
|
||||
counter++
|
||||
if counter == index+insertHolderCount+1 {
|
||||
return gconv.String(oldArg)
|
||||
}
|
||||
return s
|
||||
})
|
||||
continue
|
||||
|
||||
// The underlying driver supports time.Time/*time.Time types.
|
||||
case time.Time, *time.Time:
|
||||
newArgs = append(newArgs, oldArg)
|
||||
@ -881,9 +892,6 @@ func handleSliceAndStructArgsForSql(
|
||||
}
|
||||
}
|
||||
newArgs = append(newArgs, oldArg)
|
||||
|
||||
default:
|
||||
newArgs = append(newArgs, oldArg)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -92,3 +92,96 @@ func ReplaceIByMap(origin string, replaces map[string]string) string {
|
||||
}
|
||||
return origin
|
||||
}
|
||||
|
||||
// ReplaceFunc returns a copy of the string `origin` in which each non-overlapping substring
|
||||
// that matches the given search string is replaced by the result of function `f` applied to that substring.
|
||||
// The function `f` is called with each matching substring as its argument and must return a string to be used
|
||||
// as the replacement value.
|
||||
func ReplaceFunc(origin string, search string, f func(string) string) string {
|
||||
if search == "" {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
searchLen = len(search)
|
||||
originLen = len(origin)
|
||||
)
|
||||
// If search string is longer than origin string, no match is possible
|
||||
if searchLen > originLen {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
result strings.Builder
|
||||
lastMatch int
|
||||
currentPos int
|
||||
)
|
||||
// Pre-allocate the builder capacity to avoid reallocations
|
||||
result.Grow(originLen)
|
||||
|
||||
for currentPos < originLen {
|
||||
pos := Pos(origin[currentPos:], search)
|
||||
if pos == -1 {
|
||||
break
|
||||
}
|
||||
pos += currentPos
|
||||
// Append unmatched portion
|
||||
result.WriteString(origin[lastMatch:pos])
|
||||
// Apply replacement function and append result
|
||||
match := origin[pos : pos+searchLen]
|
||||
result.WriteString(f(match))
|
||||
// Update positions
|
||||
lastMatch = pos + searchLen
|
||||
currentPos = lastMatch
|
||||
}
|
||||
// Append remaining unmatched portion
|
||||
if lastMatch < originLen {
|
||||
result.WriteString(origin[lastMatch:])
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// ReplaceIFunc returns a copy of the string `origin` in which each non-overlapping substring
|
||||
// that matches the given search string is replaced by the result of function `f` applied to that substring.
|
||||
// The match is done case-insensitively.
|
||||
// The function `f` is called with each matching substring as its argument and must return a string to be used
|
||||
// as the replacement value.
|
||||
func ReplaceIFunc(origin string, search string, f func(string) string) string {
|
||||
if search == "" {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
searchLen = len(search)
|
||||
originLen = len(origin)
|
||||
)
|
||||
// If search string is longer than origin string, no match is possible
|
||||
if searchLen > originLen {
|
||||
return origin
|
||||
}
|
||||
var (
|
||||
result strings.Builder
|
||||
lastMatch int
|
||||
currentPos int
|
||||
)
|
||||
// Pre-allocate the builder capacity to avoid reallocations
|
||||
result.Grow(originLen)
|
||||
|
||||
for currentPos < originLen {
|
||||
pos := PosI(origin[currentPos:], search)
|
||||
if pos == -1 {
|
||||
break
|
||||
}
|
||||
pos += currentPos
|
||||
// Append unmatched portion
|
||||
result.WriteString(origin[lastMatch:pos])
|
||||
// Apply replacement function and append result
|
||||
match := origin[pos : pos+searchLen]
|
||||
result.WriteString(f(match))
|
||||
// Update positions
|
||||
lastMatch = pos + searchLen
|
||||
currentPos = lastMatch
|
||||
}
|
||||
// Append remaining unmatched portion
|
||||
if lastMatch < originLen {
|
||||
result.WriteString(origin[lastMatch:])
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ package gstr_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
@ -1018,6 +1020,51 @@ func ExampleReplaceIByMap() {
|
||||
// goframe is very nice
|
||||
}
|
||||
|
||||
func ExampleReplaceFunc() {
|
||||
str := "hello gf 2018~2020!"
|
||||
// Replace "gf" with a custom function that returns "GoFrame"
|
||||
result := gstr.ReplaceFunc(str, "gf", func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
fmt.Println(result)
|
||||
|
||||
// Replace numbers with their doubled values
|
||||
result = gstr.ReplaceFunc("1 2 3", "2", func(s string) string {
|
||||
n, _ := strconv.Atoi(s)
|
||||
return strconv.Itoa(n * 2)
|
||||
})
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// hello GoFrame 2018~2020!
|
||||
// 1 4 3
|
||||
}
|
||||
|
||||
func ExampleReplaceIFunc() {
|
||||
str := "Hello GF, hello gf, HELLO Gf!"
|
||||
// Replace any case variation of "gf" with "GoFrame"
|
||||
result := gstr.ReplaceIFunc(str, "gf", func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
fmt.Println(result)
|
||||
|
||||
// Preserve the original case of each match
|
||||
result = gstr.ReplaceIFunc(str, "gf", func(s string) string {
|
||||
if s == strings.ToUpper(s) {
|
||||
return "GOFRAME"
|
||||
}
|
||||
if s == strings.ToLower(s) {
|
||||
return "goframe"
|
||||
}
|
||||
return "GoFrame"
|
||||
})
|
||||
fmt.Println(result)
|
||||
|
||||
// Output:
|
||||
// Hello GoFrame, hello GoFrame, HELLO GoFrame!
|
||||
// Hello GOFRAME, hello goframe, HELLO GoFrame!
|
||||
}
|
||||
|
||||
// similartext
|
||||
func ExampleSimilarText() {
|
||||
var (
|
||||
|
@ -9,6 +9,7 @@
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
@ -88,3 +89,143 @@ func Test_ReplaceI_2(t *testing.T) {
|
||||
t.Assert(gstr.ReplaceI("aaa", "A", "AA", 4), `AAAAAA`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReplaceIFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
origin = "hello GF 2018~2020!"
|
||||
search = "gf"
|
||||
)
|
||||
// Simple replacement
|
||||
result := gstr.ReplaceIFunc(origin, search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "hello GoFrame 2018~2020!")
|
||||
|
||||
// Replace with original string
|
||||
result = gstr.ReplaceIFunc(origin, search, func(s string) string {
|
||||
return s
|
||||
})
|
||||
t.Assert(result, origin)
|
||||
|
||||
// Replace with empty string
|
||||
result = gstr.ReplaceIFunc(origin, search, func(s string) string {
|
||||
return ""
|
||||
})
|
||||
t.Assert(result, "hello 2018~2020!")
|
||||
|
||||
// Replace multiple occurrences with different cases
|
||||
origin = "GF is best, gf is nice, Gf is excellent"
|
||||
result = gstr.ReplaceIFunc(origin, search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "GoFrame is best, GoFrame is nice, GoFrame is excellent")
|
||||
|
||||
// Empty search string
|
||||
result = gstr.ReplaceIFunc(origin, "", func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, origin)
|
||||
|
||||
// Empty origin string
|
||||
result = gstr.ReplaceIFunc("", search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "")
|
||||
|
||||
// Replace with longer string
|
||||
result = gstr.ReplaceIFunc("GF", search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "GoFrame")
|
||||
|
||||
// Replace with shorter string
|
||||
result = gstr.ReplaceIFunc("GF", search, func(s string) string {
|
||||
return "g"
|
||||
})
|
||||
t.Assert(result, "g")
|
||||
|
||||
// Replace with mixed case patterns
|
||||
origin = "gf GF Gf gF"
|
||||
result = gstr.ReplaceIFunc(origin, search, func(s string) string {
|
||||
return strings.ToUpper(s)
|
||||
})
|
||||
t.Assert(result, "GF GF GF GF")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ReplaceFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
origin = "hello gf 2018~2020!"
|
||||
search = "gf"
|
||||
)
|
||||
// Simple replacement
|
||||
result := gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "hello GoFrame 2018~2020!")
|
||||
|
||||
// Replace with original string
|
||||
result = gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return s
|
||||
})
|
||||
t.Assert(result, origin)
|
||||
|
||||
// Replace with empty string
|
||||
result = gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return ""
|
||||
})
|
||||
t.Assert(result, "hello 2018~2020!")
|
||||
|
||||
// Replace multiple occurrences
|
||||
origin = "gf is best, gf is nice"
|
||||
result = gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "GoFrame is best, GoFrame is nice")
|
||||
|
||||
// Empty search string
|
||||
result = gstr.ReplaceFunc(origin, "", func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, origin)
|
||||
|
||||
// Empty origin string
|
||||
result = gstr.ReplaceFunc("", search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "")
|
||||
|
||||
// Case sensitive
|
||||
origin = "GF is best, gf is nice"
|
||||
result = gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "GF is best, GoFrame is nice")
|
||||
|
||||
// Replace with longer string
|
||||
result = gstr.ReplaceFunc("gf", search, func(s string) string {
|
||||
return "GoFrame"
|
||||
})
|
||||
t.Assert(result, "GoFrame")
|
||||
|
||||
// Replace with shorter string
|
||||
result = gstr.ReplaceFunc("gf", search, func(s string) string {
|
||||
return "g"
|
||||
})
|
||||
t.Assert(result, "g")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
origin = "gggg"
|
||||
search = "g"
|
||||
replace = "gg"
|
||||
)
|
||||
// Simple replacement
|
||||
result := gstr.ReplaceFunc(origin, search, func(s string) string {
|
||||
return replace
|
||||
})
|
||||
t.Assert(result, "gggggggg")
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user