diff --git a/contrib/drivers/mysql/mysql_z_unit_issue_test.go b/contrib/drivers/mysql/mysql_z_unit_issue_test.go index ff4dd70d5bd..7750606308d 100644 --- a/contrib/drivers/mysql/mysql_z_unit_issue_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_issue_test.go @@ -1091,3 +1091,37 @@ func Test_Issue2552_ClearTableFields(t *testing.T) { t.Assert(gstr.Contains(gstr.Join(sqlArray, "|"), showTableKey), true) }) } + +// https://github.com/gogf/gf/issues/2643 +func Test_Issue2643(t *testing.T) { + table := "issue2643" + array := gstr.SplitAndTrim(gtest.DataContent(`issue2643.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) { + var ( + expectKey1 = "SELECT s.name,replace(concat_ws(',',lpad(s.id, 6, '0'),s.name),',','') `code` FROM `issue2643` AS s" + expectKey2 = "SELECT CASE WHEN dept='物资部' THEN '物资部' ELSE '其他' END dept,sum(s.value) FROM `issue2643` AS s GROUP BY CASE WHEN dept='物资部' THEN '物资部' ELSE '其他' END" + ) + sqlArray, err := gdb.CatchSQL(ctx, func(ctx context.Context) error { + db.Ctx(ctx).Model(table).As("s").Fields( + "s.name", + "replace(concat_ws(',',lpad(s.id, 6, '0'),s.name),',','') `code`", + ).All() + db.Ctx(ctx).Model(table).As("s").Fields( + "CASE WHEN dept='物资部' THEN '物资部' ELSE '其他' END dept", + "sum(s.value)", + ).Group("CASE WHEN dept='物资部' THEN '物资部' ELSE '其他' END").All() + return nil + }) + t.AssertNil(err) + sqlContent := gstr.Join(sqlArray, "\n") + t.Assert(gstr.Contains(sqlContent, expectKey1), true) + t.Assert(gstr.Contains(sqlContent, expectKey2), true) + }) +} diff --git a/contrib/drivers/mysql/mysql_z_unit_model_test.go b/contrib/drivers/mysql/mysql_z_unit_model_test.go index 1a2c7e1f152..5d01841d062 100644 --- a/contrib/drivers/mysql/mysql_z_unit_model_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_model_test.go @@ -2286,7 +2286,7 @@ func Test_Model_Option_Where(t *testing.T) { n, _ := r.RowsAffected() t.Assert(n, TableSize) }) - return + gtest.C(t, func(t *gtest.T) { table := createInitTable() defer dropTable(table) diff --git a/contrib/drivers/mysql/testdata/issue2643.sql b/contrib/drivers/mysql/testdata/issue2643.sql new file mode 100644 index 00000000000..8b293402dae --- /dev/null +++ b/contrib/drivers/mysql/testdata/issue2643.sql @@ -0,0 +1,7 @@ +CREATE TABLE `issue2643` ( + `id` INT(10) NULL DEFAULT NULL, + `name` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_0900_ai_ci', + `value` INT(10) NULL DEFAULT NULL, + `dept` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_0900_ai_ci' +) +COLLATE='utf8mb4_0900_ai_ci' ENGINE=InnoDB \ No newline at end of file diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index e0ddc0329bc..f1caf4fee68 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -494,6 +494,10 @@ var ( // which is a regular field name of table. regularFieldNameRegPattern = `^[\w\.\-]+$` + // regularFieldNameWithCommaRegPattern is the regular expression pattern for one or more strings + // which are regular field names of table, multiple field names joined with char ','. + regularFieldNameWithCommaRegPattern = `^[\w\.\-,\s]+$` + // regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'. // Note that, although some databases allow char '.' in the field name, but it here does not allow '.' // in the field name as it conflicts with "db.table.field" pattern in SOME situations. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 556318d6a4e..4283030e8eb 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -767,6 +767,8 @@ func (c *Core) HasTable(name string) (bool, error) { if err != nil { return false, err } + charL, charR := c.db.GetChars() + name = gstr.Trim(name, charL+charR) for _, table := range tables { if table == name { return true, nil diff --git a/database/gdb/gdb_core_utility.go b/database/gdb/gdb_core_utility.go index 1e24e0e613e..71f0b48ea8a 100644 --- a/database/gdb/gdb_core_utility.go +++ b/database/gdb/gdb_core_utility.go @@ -93,6 +93,9 @@ func (c *Core) QuoteWord(s string) string { // // The meaning of a `string` can be considered as part of a statement string including columns. func (c *Core) QuoteString(s string) string { + if !gregex.IsMatchString(regularFieldNameWithCommaRegPattern, s) { + return s + } charLeft, charRight := c.db.GetChars() return doQuoteString(s, charLeft, charRight) } diff --git a/database/gdb/gdb_model_utility.go b/database/gdb/gdb_model_utility.go index 0873af25f2c..9a5da85225e 100644 --- a/database/gdb/gdb_model_utility.go +++ b/database/gdb/gdb_model_utility.go @@ -68,30 +68,40 @@ func (m *Model) mappingAndFilterToTableFields(table string, fields []string, fil if len(fieldsMap) == 0 { return fields } - var ( - inputFieldsArray = gstr.SplitAndTrim(gstr.Join(fields, ","), ",") - outputFieldsArray = make([]string, 0, len(inputFieldsArray)) - ) + var outputFieldsArray = make([]string, 0) fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) for k := range fieldsMap { fieldsKeyMap[k] = nil } - for _, field := range inputFieldsArray { - if _, ok := fieldsKeyMap[field]; !ok { - if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, field) { - // Eg: user.id, user.name - outputFieldsArray = append(outputFieldsArray, field) + for _, field := range fields { + var inputFieldsArray []string + if gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, field) { + inputFieldsArray = append(inputFieldsArray, field) + } else if gregex.IsMatchString(regularFieldNameWithCommaRegPattern, field) { + inputFieldsArray = gstr.SplitAndTrim(field, ",") + } else { + // Example: + // user.id, user.name + // replace(concat_ws(',',lpad(s.id, 6, '0'),s.name),',','') `code` + outputFieldsArray = append(outputFieldsArray, field) + continue + } + for _, inputField := range inputFieldsArray { + if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, inputField) { + outputFieldsArray = append(outputFieldsArray, inputField) continue - } else { - // Eg: id, name - if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" { + } + if _, ok := fieldsKeyMap[inputField]; !ok { + // Example: + // id, name + if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, inputField); foundKey != "" { outputFieldsArray = append(outputFieldsArray, foundKey) } else if !filter { - outputFieldsArray = append(outputFieldsArray, field) + outputFieldsArray = append(outputFieldsArray, inputField) } + } else { + outputFieldsArray = append(outputFieldsArray, inputField) } - } else { - outputFieldsArray = append(outputFieldsArray, field) } } return outputFieldsArray