From 921f2417bb142da6c9007f40b8587823eac49f4a Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Fri, 16 Jan 2026 23:40:18 +0800 Subject: [PATCH 1/5] feat(database/gdb): Enhanced support for automatic escaping of special characters in the WhereLike method. Signed-off-by: yuluo-yx --- database/gdb/gdb_func.go | 22 +++++++++++++++++++ database/gdb/gdb_model_builder_where.go | 6 +++-- .../gdb/gdb_model_builder_where_prefix.go | 8 +++++-- database/gdb/gdb_model_builder_whereor.go | 7 ++++-- .../gdb/gdb_model_builder_whereor_prefix.go | 8 +++++-- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 437c981f8fe..b8d8837d910 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -1015,3 +1015,25 @@ func genTableNamesCacheKey(group string) string { func genSoftTimeFieldNameTypeCacheKey(schema, table string, candidateFields []string) string { return fmt.Sprintf(`getSoftFieldNameAndType:%s#%s#%s`, schema, table, strings.Join(candidateFields, "_")) } + +// EscapeLikeString escapes special characters in a string for LIKE operations. +// It escapes '\', '%', and '_' characters to prevent them from being interpreted +// as wildcard characters in SQL LIKE statements. +// +// The function follows standard SQL escaping rules: +// - '\' becomes '\\' +// - '%' becomes '\%' +// - '_' becomes '\_' +// +// Example: +// +// EscapeLikeString("john_doe%test\\data") returns "john\\_doe\\%test\\\\data" +func EscapeLikeString(s string) string { + // Escape backslashes first to prevent double escaping + s = strings.ReplaceAll(s, "\\", "\\\\") + // Escape percent signs + s = strings.ReplaceAll(s, "%", "\\%") + // Escape underscores + s = strings.ReplaceAll(s, "_", "\\_") + return s +} diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go index 581bdec3bae..6595af468b9 100644 --- a/database/gdb/gdb_model_builder_where.go +++ b/database/gdb/gdb_model_builder_where.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" ) // doWhereType sets the condition statement for the model. The parameter `where` can be type of @@ -114,7 +115,7 @@ func (b *WhereBuilder) WhereBetween(column string, min, max any) *WhereBuilder { // WhereLike builds `column LIKE like` statement. func (b *WhereBuilder) WhereLike(column string, like string) *WhereBuilder { - return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), like) + return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(like)) } // WhereIn builds `column IN (in)` statement. @@ -138,7 +139,8 @@ func (b *WhereBuilder) WhereNotBetween(column string, min, max any) *WhereBuilde // WhereNotLike builds `column NOT LIKE like` statement. func (b *WhereBuilder) WhereNotLike(column string, like any) *WhereBuilder { - return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WhereNot builds `column != value` statement. diff --git a/database/gdb/gdb_model_builder_where_prefix.go b/database/gdb/gdb_model_builder_where_prefix.go index 67ebc254c52..f7a862416c5 100644 --- a/database/gdb/gdb_model_builder_where_prefix.go +++ b/database/gdb/gdb_model_builder_where_prefix.go @@ -6,6 +6,8 @@ package gdb +import "github.com/gogf/gf/v2/util/gconv" + // WherePrefix performs as Where, but it adds prefix to each field in where statement. // Eg: // WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid' @@ -54,7 +56,8 @@ func (b *WhereBuilder) WherePrefixBetween(prefix string, column string, min, max // WherePrefixLike builds `prefix.column LIKE like` statement. func (b *WhereBuilder) WherePrefixLike(prefix string, column string, like any) *WhereBuilder { - return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WherePrefixIn builds `prefix.column IN (in)` statement. @@ -78,7 +81,8 @@ func (b *WhereBuilder) WherePrefixNotBetween(prefix string, column string, min, // WherePrefixNotLike builds `prefix.column NOT LIKE like` statement. func (b *WhereBuilder) WherePrefixNotLike(prefix string, column string, like any) *WhereBuilder { - return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WherePrefixNot builds `prefix.column != value` statement. diff --git a/database/gdb/gdb_model_builder_whereor.go b/database/gdb/gdb_model_builder_whereor.go index 33d32f1382b..d1dd29e2caf 100644 --- a/database/gdb/gdb_model_builder_whereor.go +++ b/database/gdb/gdb_model_builder_whereor.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" ) // WhereOr adds "OR" condition to the where statement. @@ -83,7 +84,8 @@ func (b *WhereBuilder) WhereOrBetween(column string, min, max any) *WhereBuilder // WhereOrLike builds `column LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrLike(column string, like any) *WhereBuilder { - return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WhereOrIn builds `column IN (in)` statement in `OR` conditions. @@ -107,7 +109,8 @@ func (b *WhereBuilder) WhereOrNotBetween(column string, min, max any) *WhereBuil // WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions. func (b *WhereBuilder) WhereOrNotLike(column string, like any) *WhereBuilder { - return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WhereOrNotIn builds `column NOT IN (in)` statement. diff --git a/database/gdb/gdb_model_builder_whereor_prefix.go b/database/gdb/gdb_model_builder_whereor_prefix.go index 8707584049b..1de71396d83 100644 --- a/database/gdb/gdb_model_builder_whereor_prefix.go +++ b/database/gdb/gdb_model_builder_whereor_prefix.go @@ -6,6 +6,8 @@ package gdb +import "github.com/gogf/gf/v2/util/gconv" + // WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. // Eg: // WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid') @@ -56,7 +58,8 @@ func (b *WhereBuilder) WhereOrPrefixBetween(prefix string, column string, min, m // WhereOrPrefixLike builds `prefix.column LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrPrefixLike(prefix string, column string, like any) *WhereBuilder { - return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. @@ -80,7 +83,8 @@ func (b *WhereBuilder) WhereOrPrefixNotBetween(prefix string, column string, min // WhereOrPrefixNotLike builds `prefix.column NOT LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrPrefixNotLike(prefix string, column string, like any) *WhereBuilder { - return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) + likeStr := gconv.String(like) + return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) } // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. From 348ccad5ee6596720de3ddb55f9212c342452317 Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Sat, 17 Jan 2026 21:43:11 +0800 Subject: [PATCH 2/5] fix Signed-off-by: yuluo-yx --- database/gdb/gdb_func.go | 19 +++++++- database/gdb/gdb_model_builder_where.go | 6 +-- .../gdb/gdb_model_builder_where_prefix.go | 8 +--- database/gdb/gdb_model_builder_whereor.go | 7 +-- .../gdb/gdb_model_builder_whereor_prefix.go | 8 +--- database/gdb/gdb_z_like_escape_test.go | 46 +++++++++++++++++++ 6 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 database/gdb/gdb_z_like_escape_test.go diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index b8d8837d910..7286c5227c7 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -1020,14 +1020,29 @@ func genSoftTimeFieldNameTypeCacheKey(schema, table string, candidateFields []st // It escapes '\', '%', and '_' characters to prevent them from being interpreted // as wildcard characters in SQL LIKE statements. // +// This function is useful when you need to search for literal characters that would +// otherwise be treated as wildcards in LIKE patterns. Use this when accepting user +// input for LIKE operations to prevent unintended wildcard matching. +// // The function follows standard SQL escaping rules: // - '\' becomes '\\' // - '%' becomes '\%' // - '_' becomes '\_' // -// Example: +// Usage examples: +// +// // Search for exact text containing special characters +// userInput := "user%name_test" +// escaped := gdb.EscapeLikeString(userInput) // "user\\%name\\_test" +// db.Model("users").WhereLike("username", escaped) +// +// // Search for text containing special characters with wildcards +// userInput := "user%name" +// escaped := gdb.EscapeLikeString(userInput) // "user\\%name" +// db.Model("users").WhereLike("username", "%"+escaped+"%") // LIKE '%user\%name%' // -// EscapeLikeString("john_doe%test\\data") returns "john\\_doe\\%test\\\\data" +// // Normal wildcard usage (do NOT escape) +// db.Model("users").WhereLike("username", "user%") // LIKE 'user%' - matches userABC, user123, etc. func EscapeLikeString(s string) string { // Escape backslashes first to prevent double escaping s = strings.ReplaceAll(s, "\\", "\\\\") diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go index 6595af468b9..581bdec3bae 100644 --- a/database/gdb/gdb_model_builder_where.go +++ b/database/gdb/gdb_model_builder_where.go @@ -10,7 +10,6 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gconv" ) // doWhereType sets the condition statement for the model. The parameter `where` can be type of @@ -115,7 +114,7 @@ func (b *WhereBuilder) WhereBetween(column string, min, max any) *WhereBuilder { // WhereLike builds `column LIKE like` statement. func (b *WhereBuilder) WhereLike(column string, like string) *WhereBuilder { - return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(like)) + return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), like) } // WhereIn builds `column IN (in)` statement. @@ -139,8 +138,7 @@ func (b *WhereBuilder) WhereNotBetween(column string, min, max any) *WhereBuilde // WhereNotLike builds `column NOT LIKE like` statement. func (b *WhereBuilder) WhereNotLike(column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) } // WhereNot builds `column != value` statement. diff --git a/database/gdb/gdb_model_builder_where_prefix.go b/database/gdb/gdb_model_builder_where_prefix.go index f7a862416c5..67ebc254c52 100644 --- a/database/gdb/gdb_model_builder_where_prefix.go +++ b/database/gdb/gdb_model_builder_where_prefix.go @@ -6,8 +6,6 @@ package gdb -import "github.com/gogf/gf/v2/util/gconv" - // WherePrefix performs as Where, but it adds prefix to each field in where statement. // Eg: // WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid' @@ -56,8 +54,7 @@ func (b *WhereBuilder) WherePrefixBetween(prefix string, column string, min, max // WherePrefixLike builds `prefix.column LIKE like` statement. func (b *WhereBuilder) WherePrefixLike(prefix string, column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } // WherePrefixIn builds `prefix.column IN (in)` statement. @@ -81,8 +78,7 @@ func (b *WhereBuilder) WherePrefixNotBetween(prefix string, column string, min, // WherePrefixNotLike builds `prefix.column NOT LIKE like` statement. func (b *WhereBuilder) WherePrefixNotLike(prefix string, column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } // WherePrefixNot builds `prefix.column != value` statement. diff --git a/database/gdb/gdb_model_builder_whereor.go b/database/gdb/gdb_model_builder_whereor.go index d1dd29e2caf..33d32f1382b 100644 --- a/database/gdb/gdb_model_builder_whereor.go +++ b/database/gdb/gdb_model_builder_whereor.go @@ -10,7 +10,6 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gconv" ) // WhereOr adds "OR" condition to the where statement. @@ -84,8 +83,7 @@ func (b *WhereBuilder) WhereOrBetween(column string, min, max any) *WhereBuilder // WhereOrLike builds `column LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrLike(column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), like) } // WhereOrIn builds `column IN (in)` statement in `OR` conditions. @@ -109,8 +107,7 @@ func (b *WhereBuilder) WhereOrNotBetween(column string, min, max any) *WhereBuil // WhereOrNotLike builds `column NOT LIKE like` statement in `OR` conditions. func (b *WhereBuilder) WhereOrNotLike(column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) } // WhereOrNotIn builds `column NOT IN (in)` statement. diff --git a/database/gdb/gdb_model_builder_whereor_prefix.go b/database/gdb/gdb_model_builder_whereor_prefix.go index 1de71396d83..8707584049b 100644 --- a/database/gdb/gdb_model_builder_whereor_prefix.go +++ b/database/gdb/gdb_model_builder_whereor_prefix.go @@ -6,8 +6,6 @@ package gdb -import "github.com/gogf/gf/v2/util/gconv" - // WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. // Eg: // WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid') @@ -58,8 +56,7 @@ func (b *WhereBuilder) WhereOrPrefixBetween(prefix string, column string, min, m // WhereOrPrefixLike builds `prefix.column LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrPrefixLike(prefix string, column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. @@ -83,8 +80,7 @@ func (b *WhereBuilder) WhereOrPrefixNotBetween(prefix string, column string, min // WhereOrPrefixNotLike builds `prefix.column NOT LIKE 'like'` statement in `OR` conditions. func (b *WhereBuilder) WhereOrPrefixNotLike(prefix string, column string, like any) *WhereBuilder { - likeStr := gconv.String(like) - return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(likeStr)) + return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. diff --git a/database/gdb/gdb_z_like_escape_test.go b/database/gdb/gdb_z_like_escape_test.go new file mode 100644 index 00000000000..0bf44885ccc --- /dev/null +++ b/database/gdb/gdb_z_like_escape_test.go @@ -0,0 +1,46 @@ +package gdb_test + +import ( + "testing" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/test/gtest" +) + +func ExampleEscapeLikeString() { + // Escape special characters in user input for LIKE operations + userInput := "test%_data\\with_special" + escaped := gdb.EscapeLikeString(userInput) + + // The escaped string can now be safely used in LIKE patterns + // Original: "test%_data\with_special" + // Escaped: "test\\%\\_data\\\\with\\_special" + + // Usage example with WhereLike + // db.Model("table").WhereLike("column", "%"+escaped+"%") + + // Output: test\%\_data\\with\_special + print(escaped) +} + +func Test_EscapeLikeString(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Test escaping backslash + t.Assert(gdb.EscapeLikeString("test\\data"), "test\\\\data") + + // Test escaping percent + t.Assert(gdb.EscapeLikeString("test%data"), "test\\%data") + + // Test escaping underscore + t.Assert(gdb.EscapeLikeString("test_data"), "test\\_data") + + // Test escaping all special characters + t.Assert(gdb.EscapeLikeString("test\\%_data"), "test\\\\\\%\\_data") + + // Test empty string + t.Assert(gdb.EscapeLikeString(""), "") + + // Test string with no special characters + t.Assert(gdb.EscapeLikeString("normal"), "normal") + }) +} From b310ce066ed447a41d4f5b21e04ce1b22c6b47a5 Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Sat, 17 Jan 2026 21:49:17 +0800 Subject: [PATCH 3/5] fix Signed-off-by: yuluo-yx --- database/gdb/gdb_model_builder_where.go | 19 +++++++++++++++++++ .../gdb/gdb_model_builder_where_prefix.go | 14 ++++++++++++++ database/gdb/gdb_model_builder_whereor.go | 13 +++++++++++++ .../gdb/gdb_model_builder_whereor_prefix.go | 14 ++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go index 581bdec3bae..19304dc5159 100644 --- a/database/gdb/gdb_model_builder_where.go +++ b/database/gdb/gdb_model_builder_where.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" ) // doWhereType sets the condition statement for the model. The parameter `where` can be type of @@ -117,6 +118,18 @@ func (b *WhereBuilder) WhereLike(column string, like string) *WhereBuilder { return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), like) } +// WhereLikeLiteral builds `column LIKE like` statement with automatic escaping of special characters. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +// Use this method when you want to search for exact text that may contain LIKE special characters. +// +// Example: +// +// db.Model("user").WhereLikeLiteral("name", "john%doe_123") // Will search for exact string "john%doe_123" +// db.Model("user").WhereLike("name", "john%") // Will search using % as wildcard +func (b *WhereBuilder) WhereLikeLiteral(column string, like string) *WhereBuilder { + return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(like)) +} + // WhereIn builds `column IN (in)` statement. func (b *WhereBuilder) WhereIn(column string, in any) *WhereBuilder { return b.doWherefType(whereHolderTypeIn, `%s IN (?)`, b.model.QuoteWord(column), in) @@ -141,6 +154,12 @@ func (b *WhereBuilder) WhereNotLike(column string, like any) *WhereBuilder { return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) } +// WhereNotLikeLiteral builds `column NOT LIKE like` statement with automatic escaping of special characters. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WhereNotLikeLiteral(column string, like any) *WhereBuilder { + return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WhereNot builds `column != value` statement. func (b *WhereBuilder) WhereNot(column string, value any) *WhereBuilder { return b.Wheref(`%s != ?`, b.model.QuoteWord(column), value) diff --git a/database/gdb/gdb_model_builder_where_prefix.go b/database/gdb/gdb_model_builder_where_prefix.go index 67ebc254c52..5df498008f8 100644 --- a/database/gdb/gdb_model_builder_where_prefix.go +++ b/database/gdb/gdb_model_builder_where_prefix.go @@ -6,6 +6,8 @@ package gdb +import "github.com/gogf/gf/v2/util/gconv" + // WherePrefix performs as Where, but it adds prefix to each field in where statement. // Eg: // WherePrefix("order", "status", "paid") => WHERE `order`.`status`='paid' @@ -57,6 +59,12 @@ func (b *WhereBuilder) WherePrefixLike(prefix string, column string, like any) * return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } +// WherePrefixLikeLiteral builds `prefix.column LIKE like` statement with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WherePrefixLikeLiteral(prefix string, column string, like any) *WhereBuilder { + return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WherePrefixIn builds `prefix.column IN (in)` statement. func (b *WhereBuilder) WherePrefixIn(prefix string, column string, in any) *WhereBuilder { return b.doWherefType(whereHolderTypeIn, `%s.%s IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) @@ -81,6 +89,12 @@ func (b *WhereBuilder) WherePrefixNotLike(prefix string, column string, like any return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } +// WherePrefixNotLikeLiteral builds `prefix.column NOT LIKE like` statement with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WherePrefixNotLikeLiteral(prefix string, column string, like any) *WhereBuilder { + return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WherePrefixNot builds `prefix.column != value` statement. func (b *WhereBuilder) WherePrefixNot(prefix string, column string, value any) *WhereBuilder { return b.Wheref(`%s.%s != ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), value) diff --git a/database/gdb/gdb_model_builder_whereor.go b/database/gdb/gdb_model_builder_whereor.go index 33d32f1382b..2adc71a1bd2 100644 --- a/database/gdb/gdb_model_builder_whereor.go +++ b/database/gdb/gdb_model_builder_whereor.go @@ -10,6 +10,7 @@ import ( "fmt" "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" ) // WhereOr adds "OR" condition to the where statement. @@ -86,6 +87,12 @@ func (b *WhereBuilder) WhereOrLike(column string, like any) *WhereBuilder { return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), like) } +// WhereOrLikeLiteral builds `column LIKE 'like'` statement in `OR` conditions with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WhereOrLikeLiteral(column string, like any) *WhereBuilder { + return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WhereOrIn builds `column IN (in)` statement in `OR` conditions. func (b *WhereBuilder) WhereOrIn(column string, in any) *WhereBuilder { return b.doWhereOrfType(whereHolderTypeIn, `%s IN (?)`, b.model.QuoteWord(column), in) @@ -110,6 +117,12 @@ func (b *WhereBuilder) WhereOrNotLike(column string, like any) *WhereBuilder { return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), like) } +// WhereOrNotLikeLiteral builds `column NOT LIKE like` statement in `OR` conditions with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WhereOrNotLikeLiteral(column string, like any) *WhereBuilder { + return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WhereOrNotIn builds `column NOT IN (in)` statement. func (b *WhereBuilder) WhereOrNotIn(column string, in any) *WhereBuilder { return b.doWhereOrfType(whereHolderTypeIn, `%s NOT IN (?)`, b.model.QuoteWord(column), in) diff --git a/database/gdb/gdb_model_builder_whereor_prefix.go b/database/gdb/gdb_model_builder_whereor_prefix.go index 8707584049b..74429c883ef 100644 --- a/database/gdb/gdb_model_builder_whereor_prefix.go +++ b/database/gdb/gdb_model_builder_whereor_prefix.go @@ -6,6 +6,8 @@ package gdb +import "github.com/gogf/gf/v2/util/gconv" + // WhereOrPrefix performs as WhereOr, but it adds prefix to each field in where statement. // Eg: // WhereOrPrefix("order", "status", "paid") => WHERE xxx OR (`order`.`status`='paid') @@ -59,6 +61,12 @@ func (b *WhereBuilder) WhereOrPrefixLike(prefix string, column string, like any) return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } +// WhereOrPrefixLikeLiteral builds `prefix.column LIKE 'like'` statement in `OR` conditions with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WhereOrPrefixLikeLiteral(prefix string, column string, like any) *WhereBuilder { + return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. func (b *WhereBuilder) WhereOrPrefixIn(prefix string, column string, in any) *WhereBuilder { return b.doWhereOrfType(whereHolderTypeIn, `%s.%s IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) @@ -83,6 +91,12 @@ func (b *WhereBuilder) WhereOrPrefixNotLike(prefix string, column string, like a return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), like) } +// WhereOrPrefixNotLikeLiteral builds `prefix.column NOT LIKE 'like'` statement in `OR` conditions with automatic escaping. +// This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. +func (b *WhereBuilder) WhereOrPrefixNotLikeLiteral(prefix string, column string, like any) *WhereBuilder { + return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) +} + // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. func (b *WhereBuilder) WhereOrPrefixNotIn(prefix string, column string, in any) *WhereBuilder { return b.doWhereOrfType(whereHolderTypeIn, `%s.%s NOT IN (?)`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), in) From 2348697b5edb3f2e369dcd6955f3fa64e522cf43 Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Sat, 17 Jan 2026 21:50:53 +0800 Subject: [PATCH 4/5] chore: add license Signed-off-by: yuluo-yx --- database/gdb/gdb_z_like_escape_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_z_like_escape_test.go b/database/gdb/gdb_z_like_escape_test.go index 0bf44885ccc..1a654dcf44c 100644 --- a/database/gdb/gdb_z_like_escape_test.go +++ b/database/gdb/gdb_z_like_escape_test.go @@ -1,6 +1,13 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package gdb_test import ( + "fmt" "testing" "github.com/gogf/gf/v2/database/gdb" @@ -20,7 +27,7 @@ func ExampleEscapeLikeString() { // db.Model("table").WhereLike("column", "%"+escaped+"%") // Output: test\%\_data\\with\_special - print(escaped) + fmt.Println(escaped) } func Test_EscapeLikeString(t *testing.T) { From de7724bb70bbbe7b659b6f2a08cea5326998f20a Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Thu, 22 Jan 2026 20:57:13 +0800 Subject: [PATCH 5/5] feat: add ut Signed-off-by: yuluo-yx --- .../mysql/mysql_z_unit_model_where_test.go | 80 +++++++++++++++++++ database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model_builder_where.go | 4 +- .../gdb/gdb_model_builder_where_prefix.go | 4 +- database/gdb/gdb_model_builder_whereor.go | 4 +- .../gdb/gdb_model_builder_whereor_prefix.go | 4 +- database/gdb/gdb_model_whereor_prefix.go | 12 +++ database/gdb/gdb_z_like_escape_test.go | 53 ------------ 8 files changed, 101 insertions(+), 62 deletions(-) delete mode 100644 database/gdb/gdb_z_like_escape_test.go diff --git a/contrib/drivers/mysql/mysql_z_unit_model_where_test.go b/contrib/drivers/mysql/mysql_z_unit_model_where_test.go index c0007c6885a..f030cbee593 100644 --- a/contrib/drivers/mysql/mysql_z_unit_model_where_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_model_where_test.go @@ -1053,6 +1053,86 @@ func Test_Model_WherePrefixLike(t *testing.T) { }) } +func Test_Model_WhereOrPrefixLike(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + WhereOrPrefix(table1, g.Map{ + "id": g.Slice{1, 2}, + }). + WhereOrPrefixLike(table2, "nickname", "name_3%"). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 3) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + t.Assert(r[2]["id"], "3") + }) +} + +func Test_Model_WhereOrPrefixLikeLiteral(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + WhereOrPrefix(table1, g.Map{ + "id": g.Slice{1, 2}, + }). + WhereOrPrefixLikeLiteral(table2, "nickname", "name_3"). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 3) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + t.Assert(r[2]["id"], "3") + }) +} + +func Test_Model_WhereOrPrefixNotLikeLiteral(t *testing.T) { + var ( + table1 = gtime.TimestampNanoStr() + "_table1" + table2 = gtime.TimestampNanoStr() + "_table2" + ) + createInitTable(table1) + defer dropTable(table1) + createInitTable(table2) + defer dropTable(table2) + + gtest.C(t, func(t *gtest.T) { + r, err := db.Model(table1). + FieldsPrefix(table1, "*"). + LeftJoinOnField(table2, "id"). + WhereOrPrefix(table1, g.Map{ + "id": g.Slice{1, 2}, + }). + WhereOrPrefixNotLikeLiteral(table2, "nickname", "name_1"). + Order("id asc").All() + t.AssertNil(err) + t.Assert(len(r), 10) + t.Assert(r[0]["id"], "1") + t.Assert(r[1]["id"], "2") + }) +} + func Test_Model_WhereExists(t *testing.T) { table := createInitTable() defer dropTable(table) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 7286c5227c7..b58983019a5 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -1043,7 +1043,7 @@ func genSoftTimeFieldNameTypeCacheKey(schema, table string, candidateFields []st // // // Normal wildcard usage (do NOT escape) // db.Model("users").WhereLike("username", "user%") // LIKE 'user%' - matches userABC, user123, etc. -func EscapeLikeString(s string) string { +func escapeLikeString(s string) string { // Escape backslashes first to prevent double escaping s = strings.ReplaceAll(s, "\\", "\\\\") // Escape percent signs diff --git a/database/gdb/gdb_model_builder_where.go b/database/gdb/gdb_model_builder_where.go index 19304dc5159..a72ca0937f5 100644 --- a/database/gdb/gdb_model_builder_where.go +++ b/database/gdb/gdb_model_builder_where.go @@ -127,7 +127,7 @@ func (b *WhereBuilder) WhereLike(column string, like string) *WhereBuilder { // db.Model("user").WhereLikeLiteral("name", "john%doe_123") // Will search for exact string "john%doe_123" // db.Model("user").WhereLike("name", "john%") // Will search using % as wildcard func (b *WhereBuilder) WhereLikeLiteral(column string, like string) *WhereBuilder { - return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(like)) + return b.Wheref(`%s LIKE ?`, b.model.QuoteWord(column), escapeLikeString(like)) } // WhereIn builds `column IN (in)` statement. @@ -157,7 +157,7 @@ func (b *WhereBuilder) WhereNotLike(column string, like any) *WhereBuilder { // WhereNotLikeLiteral builds `column NOT LIKE like` statement with automatic escaping of special characters. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WhereNotLikeLiteral(column string, like any) *WhereBuilder { - return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.Wheref(`%s NOT LIKE ?`, b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WhereNot builds `column != value` statement. diff --git a/database/gdb/gdb_model_builder_where_prefix.go b/database/gdb/gdb_model_builder_where_prefix.go index 5df498008f8..a66f9a65ff9 100644 --- a/database/gdb/gdb_model_builder_where_prefix.go +++ b/database/gdb/gdb_model_builder_where_prefix.go @@ -62,7 +62,7 @@ func (b *WhereBuilder) WherePrefixLike(prefix string, column string, like any) * // WherePrefixLikeLiteral builds `prefix.column LIKE like` statement with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WherePrefixLikeLiteral(prefix string, column string, like any) *WhereBuilder { - return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.Wheref(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WherePrefixIn builds `prefix.column IN (in)` statement. @@ -92,7 +92,7 @@ func (b *WhereBuilder) WherePrefixNotLike(prefix string, column string, like any // WherePrefixNotLikeLiteral builds `prefix.column NOT LIKE like` statement with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WherePrefixNotLikeLiteral(prefix string, column string, like any) *WhereBuilder { - return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.Wheref(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WherePrefixNot builds `prefix.column != value` statement. diff --git a/database/gdb/gdb_model_builder_whereor.go b/database/gdb/gdb_model_builder_whereor.go index 2adc71a1bd2..f178f8d8dfe 100644 --- a/database/gdb/gdb_model_builder_whereor.go +++ b/database/gdb/gdb_model_builder_whereor.go @@ -90,7 +90,7 @@ func (b *WhereBuilder) WhereOrLike(column string, like any) *WhereBuilder { // WhereOrLikeLiteral builds `column LIKE 'like'` statement in `OR` conditions with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WhereOrLikeLiteral(column string, like any) *WhereBuilder { - return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.WhereOrf(`%s LIKE ?`, b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WhereOrIn builds `column IN (in)` statement in `OR` conditions. @@ -120,7 +120,7 @@ func (b *WhereBuilder) WhereOrNotLike(column string, like any) *WhereBuilder { // WhereOrNotLikeLiteral builds `column NOT LIKE like` statement in `OR` conditions with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WhereOrNotLikeLiteral(column string, like any) *WhereBuilder { - return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.WhereOrf(`%s NOT LIKE ?`, b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WhereOrNotIn builds `column NOT IN (in)` statement. diff --git a/database/gdb/gdb_model_builder_whereor_prefix.go b/database/gdb/gdb_model_builder_whereor_prefix.go index 74429c883ef..60908f80a73 100644 --- a/database/gdb/gdb_model_builder_whereor_prefix.go +++ b/database/gdb/gdb_model_builder_whereor_prefix.go @@ -64,7 +64,7 @@ func (b *WhereBuilder) WhereOrPrefixLike(prefix string, column string, like any) // WhereOrPrefixLikeLiteral builds `prefix.column LIKE 'like'` statement in `OR` conditions with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WhereOrPrefixLikeLiteral(prefix string, column string, like any) *WhereBuilder { - return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.WhereOrf(`%s.%s LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. @@ -94,7 +94,7 @@ func (b *WhereBuilder) WhereOrPrefixNotLike(prefix string, column string, like a // WhereOrPrefixNotLikeLiteral builds `prefix.column NOT LIKE 'like'` statement in `OR` conditions with automatic escaping. // This method automatically escapes '%', '_', and '\' characters in the like parameter to treat them as literal characters. func (b *WhereBuilder) WhereOrPrefixNotLikeLiteral(prefix string, column string, like any) *WhereBuilder { - return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), EscapeLikeString(gconv.String(like))) + return b.WhereOrf(`%s.%s NOT LIKE ?`, b.model.QuoteWord(prefix), b.model.QuoteWord(column), escapeLikeString(gconv.String(like))) } // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. diff --git a/database/gdb/gdb_model_whereor_prefix.go b/database/gdb/gdb_model_whereor_prefix.go index 5b2adb46d76..ea0f7481840 100644 --- a/database/gdb/gdb_model_whereor_prefix.go +++ b/database/gdb/gdb_model_whereor_prefix.go @@ -48,6 +48,12 @@ func (m *Model) WhereOrPrefixLike(prefix string, column string, like any) *Model return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLike(prefix, column, like)) } +// WhereOrPrefixLikeLiteral builds `prefix.column LIKE 'like'` statement in `OR` conditions with automatic escaping. +// See WhereBuilder.WhereOrPrefixLikeLiteral. +func (m *Model) WhereOrPrefixLikeLiteral(prefix string, column string, like any) *Model { + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixLikeLiteral(prefix, column, like)) +} + // WhereOrPrefixIn builds `prefix.column IN (in)` statement in `OR` conditions. // See WhereBuilder.WhereOrPrefixIn. func (m *Model) WhereOrPrefixIn(prefix string, column string, in any) *Model { @@ -72,6 +78,12 @@ func (m *Model) WhereOrPrefixNotLike(prefix string, column string, like any) *Mo return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotLike(prefix, column, like)) } +// WhereOrPrefixNotLikeLiteral builds `prefix.column NOT LIKE 'like'` statement in `OR` conditions with automatic escaping. +// See WhereBuilder.WhereOrPrefixNotLikeLiteral. +func (m *Model) WhereOrPrefixNotLikeLiteral(prefix string, column string, like any) *Model { + return m.callWhereBuilder(m.whereBuilder.WhereOrPrefixNotLikeLiteral(prefix, column, like)) +} + // WhereOrPrefixNotIn builds `prefix.column NOT IN (in)` statement. // See WhereBuilder.WhereOrPrefixNotIn. func (m *Model) WhereOrPrefixNotIn(prefix string, column string, in any) *Model { diff --git a/database/gdb/gdb_z_like_escape_test.go b/database/gdb/gdb_z_like_escape_test.go deleted file mode 100644 index 1a654dcf44c..00000000000 --- a/database/gdb/gdb_z_like_escape_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gdb_test - -import ( - "fmt" - "testing" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/test/gtest" -) - -func ExampleEscapeLikeString() { - // Escape special characters in user input for LIKE operations - userInput := "test%_data\\with_special" - escaped := gdb.EscapeLikeString(userInput) - - // The escaped string can now be safely used in LIKE patterns - // Original: "test%_data\with_special" - // Escaped: "test\\%\\_data\\\\with\\_special" - - // Usage example with WhereLike - // db.Model("table").WhereLike("column", "%"+escaped+"%") - - // Output: test\%\_data\\with\_special - fmt.Println(escaped) -} - -func Test_EscapeLikeString(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - // Test escaping backslash - t.Assert(gdb.EscapeLikeString("test\\data"), "test\\\\data") - - // Test escaping percent - t.Assert(gdb.EscapeLikeString("test%data"), "test\\%data") - - // Test escaping underscore - t.Assert(gdb.EscapeLikeString("test_data"), "test\\_data") - - // Test escaping all special characters - t.Assert(gdb.EscapeLikeString("test\\%_data"), "test\\\\\\%\\_data") - - // Test empty string - t.Assert(gdb.EscapeLikeString(""), "") - - // Test string with no special characters - t.Assert(gdb.EscapeLikeString("normal"), "normal") - }) -}