diff --git a/.github/workflows/gf.yml b/.github/workflows/gf.yml index ede3e6498c1..d62b3fc9816 100644 --- a/.github/workflows/gf.yml +++ b/.github/workflows/gf.yml @@ -122,6 +122,7 @@ jobs: - 1521:1521 # dm8 server + # docker run -d --name dm -p 5236:5236 loads/dm:v8.1.2.128_ent_x86_64_ctm_pack4 dm-server: image: loads/dm:v8.1.2.128_ent_x86_64_ctm_pack4 ports: diff --git a/contrib/drivers/dm/dm.go b/contrib/drivers/dm/dm.go index 71e0a85f133..8e955e16d2f 100644 --- a/contrib/drivers/dm/dm.go +++ b/contrib/drivers/dm/dm.go @@ -176,9 +176,9 @@ func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args [ // There should be no need to capitalize, because it has been done from field processing before newSql, err = gregex.ReplaceString(`["\n\t]`, "", sql) newSql = gstr.ReplaceI(newSql, "GROUP_CONCAT", "WM_CONCAT") - // g.Dump("Driver.DoFilter()::newSql", newSql) + // gutil.Dump("Driver.DoFilter()::newSql", newSql) newArgs = args - // g.Dump("Driver.DoFilter()::newArgs", newArgs) + // gutil.Dump("Driver.DoFilter()::newArgs", newArgs) return } diff --git a/contrib/drivers/dm/dm_z_basic_test.go b/contrib/drivers/dm/dm_z_basic_test.go index 5c27bd5f71a..5f4bc0dd28f 100644 --- a/contrib/drivers/dm/dm_z_basic_test.go +++ b/contrib/drivers/dm/dm_z_basic_test.go @@ -582,125 +582,3 @@ func Test_Empty_Slice_Argument(t *testing.T) { t.Assert(len(result), 0) }) } - -// func Test_GROUP_CONCAT(t *testing.T) { -// gtest.C(t, func(t *gtest.T) { -// type GroupIdAndUserIDsInfo struct { -// GroupID int64 -// UserIDs string -// } -// result := make([]GroupIdAndUserIDsInfo, 0) - -// model := db.Model("t_inf_group", "groupinfo").Fields("groupinfo.id as group_id", "GROUP_CONCAT(userinfo.id) as user_ids") -// model.InnerJoin("t_lin_user_group", "lin", "groupinfo.id = lin.group_id") -// model.InnerJoin("t_inf_user", "userinfo", "lin.user_id = userinfo.id") -// model.Where("groupinfo.enabled", 1).Where("groupinfo.deleted", 0) -// model.Where("userinfo.enabled", 1).Where("userinfo.deleted", 0) -// model.Group("groupinfo.id") - -// err := model.Scan(&result) -// gtest.Assert(err, nil) -// g.Dump(result) -// }) -// } - -// func TestGroup(t *testing.T) { -// gtest.C(t, func(t *gtest.T) { -// type GroupListResult struct { -// ID int64 `json:"group_id"` -// GroupName string `json:"group_name"` -// CategoryName string `json:"category_name"` -// Description string `json:"description"` -// RoleName string `json:"role_name"` -// UserIDs []string `json:"user_ids"` -// Enabled int64 `json:"enabled"` -// CreatedTime string `json:"created_time"` -// UpdateTime string `json:"updated_time"` -// } -// result := make([]GroupListResult, 0) - -// model := db.Model("t_inf_group", "groupinfo") -// model.LeftJoin("t_inf_group_category", "category", "groupinfo.category_id=category.id and (category.enabled = 1) and (category.deleted = 0)"). -// Where("groupinfo.deleted", 0). -// Where("groupinfo.enabled", 1) - -// total, err := model.Count() -// gtest.Assert(err, nil) -// model.Fields("distinct groupinfo.id, groupinfo.group_name, groupinfo.enabled, ifnull(category.category_name,'') as category_name", "groupinfo.created_time", "groupinfo.updated_time", "groupinfo.description") -// err = model.Order("groupinfo.updated_time desc").Page(1, 100).Scan(&result) -// gtest.Assert(err, nil) -// g.Dump(result) -// g.Dump(total) -// }) - -// gtest.C(t, func(t *gtest.T) { -// type GroupListByUserIdResult struct { -// ID int64 `json:"group_id"` -// GroupName string `json:"group_name"` -// CategoryName string `json:"category_name"` -// RoleName string `json:"role_name"` -// } -// result := make([]*GroupListByUserIdResult, 0) - -// model := db.Model("t_inf_group", "groupinfo").Fields("distinct groupinfo.id, groupinfo.group_name, groupinfo.enabled, category.category_name,groupinfo.updated_time") - -// model.LeftJoin("t_inf_group_category", "category", "groupinfo.category_id=category.id and (category.enabled = 1) and (category.deleted = 0)") - -// // if userId != 0 { -// // model.InnerJoin(grouptype.TLINUSERGROUP, "ug", "groupinfo.id=ug.group_id") -// // model.InnerJoin(grouptype.TINFUSER, "u", "u.id=ug.user_id") -// // model.Where("u.id = ?", userId).Where("u.deleted", consts.DataDeletedFalse) -// // } - -// model.Where("groupinfo.enabled", 1).Where("groupinfo.deleted", 0) - -// err := model.Order("groupinfo.updated_time desc").Scan(&result) -// // -// gtest.Assert(err, nil) -// g.Dump(result) -// }) -// gtest.C(t, func(t *gtest.T) { - -// model := db.Model("t_inf_role", "role").Fields("role.role_name", "role.id") -// model.RightJoin("t_lin_group_role", "link", "link.role_id=role.id") -// // model.Where("link.group_id", gid) -// model.Where("role.deleted", 0) - -// record, err := model.One() -// gtest.Assert(err, nil) -// g.Dump(record) - -// }) -// gtest.C(t, func(t *gtest.T) { -// type GroupInfos struct { -// RoleName string `orm:"role_name"` -// RoleID int64 `orm:"id"` -// GroupID int64 `orm:"group_id"` -// } -// result := make([]GroupInfos, 0) - -// model := db.Model("t_inf_role", "role").Fields("role.id", "role.role_name", "link.group_id") -// model.RightJoin("t_lin_group_role", "link", "link.role_id=role.id") -// model.Where("role.enabled", 1).Where("role.deleted", 0) -// err := model.Scan(&result) -// gtest.Assert(err, nil) -// g.Dump(result) -// }) -// gtest.C(t, func(t *gtest.T) { -// type GroupIdAndRoleNameInfo struct { -// GroupID int64 -// RoleID int64 -// RoleName string -// } -// result := make([]GroupIdAndRoleNameInfo, 0) -// model := db.Model("t_inf_group", "groupinfo").Fields("groupinfo.id as group_id", "lin.role_id", "role.role_name") -// model.InnerJoin("t_lin_group_role", "lin", "groupinfo.id = lin.group_id") -// model.InnerJoin("t_inf_role", "role", "lin.role_id = role.id") -// model.Where("groupinfo.enabled", 1).Where("groupinfo.deleted", 0) -// model.Where("role.enabled", 1).Where("role.deleted", 0) - -// err2 := model.Scan(&result) -// gtest.Assert(err2, nil) -// g.Dump(result) -// }) -// } diff --git a/contrib/drivers/dm/go.mod b/contrib/drivers/dm/go.mod index dcd43ee147c..dee9db964b7 100644 --- a/contrib/drivers/dm/go.mod +++ b/contrib/drivers/dm/go.mod @@ -5,6 +5,6 @@ go 1.15 replace github.com/gogf/gf/v2 => ../../../ require ( - gitee.com/chunanyong/dm v1.8.6 + gitee.com/chunanyong/dm v1.8.11 github.com/gogf/gf/v2 v2.0.0 ) diff --git a/contrib/drivers/dm/go.sum b/contrib/drivers/dm/go.sum index 7524fc3653a..b7e652d7c4f 100644 --- a/contrib/drivers/dm/go.sum +++ b/contrib/drivers/dm/go.sum @@ -1,5 +1,5 @@ -gitee.com/chunanyong/dm v1.8.6 h1:5UnOCW1f2+LYiSQvuHiloS6OTMnZAtjRQ4woi9i6QY4= -gitee.com/chunanyong/dm v1.8.6/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg= +gitee.com/chunanyong/dm v1.8.11 h1:JPwiS1PqHObo4QFodruLR8WOhLP+7Y/EKGGu2BJ5SJI= +gitee.com/chunanyong/dm v1.8.11/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= diff --git a/contrib/drivers/mysql/mysql_feature_time_maintain_test.go b/contrib/drivers/mysql/mysql_feature_time_maintain_test.go index 80aa74fc84c..110da78c296 100644 --- a/contrib/drivers/mysql/mysql_feature_time_maintain_test.go +++ b/contrib/drivers/mysql/mysql_feature_time_maintain_test.go @@ -646,7 +646,7 @@ CREATE TABLE %s ( t.Assert(one["name"], "name_1") // Soft deleting. - r, err = db.Model(table1).Delete() + r, err = db.Model(table1).Where(1).Delete() t.AssertNil(err) n, _ = r.RowsAffected() t.Assert(n, 1) diff --git a/contrib/drivers/mysql/mysql_issue_test.go b/contrib/drivers/mysql/mysql_issue_test.go index 9b19dbec2be..5e599cc104c 100644 --- a/contrib/drivers/mysql/mysql_issue_test.go +++ b/contrib/drivers/mysql/mysql_issue_test.go @@ -633,3 +633,34 @@ CREATE TABLE %s ( t.Assert(all[0]["nickname"], "name_2") }) } + +// https://github.com/gogf/gf/issues/2427 +func Test_Issue2427(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + table := "demo_" + guid.S() + if _, err := db.Exec(ctx, fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'User ID', + passport varchar(45) NOT NULL COMMENT 'User Passport', + password varchar(45) NOT NULL COMMENT 'User Password', + nickname varchar(45) NOT NULL COMMENT 'User Nickname', + create_at datetime DEFAULT NULL COMMENT 'Created Time', + update_at datetime DEFAULT NULL COMMENT 'Updated Time', + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table, + )); err != nil { + t.AssertNil(err) + } + defer dropTable(table) + + _, err1 := db.Model(table).Delete() + t.Assert(err1, `there should be WHERE condition statement for DELETE operation`) + + _, err2 := db.Model(table).Where(g.Map{}).Delete() + t.Assert(err2, `there should be WHERE condition statement for DELETE operation`) + + _, err3 := db.Model(table).Where(1).Delete() + t.AssertNil(err3) + }) +} diff --git a/database/gdb/gdb_model_delete.go b/database/gdb/gdb_model_delete.go index 32ccd0cabf7..4dd05ef98ff 100644 --- a/database/gdb/gdb_model_delete.go +++ b/database/gdb/gdb_model_delete.go @@ -9,11 +9,12 @@ package gdb import ( "database/sql" "fmt" - "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/internal/intlog" "github.com/gogf/gf/v2/text/gstr" + + "github.com/gogf/gf/v2/os/gtime" ) // Delete does "DELETE FROM ... " statement for the model. @@ -32,9 +33,25 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { var ( fieldNameDelete = m.getSoftFieldNameDeleted("", m.tablesInit) conditionWhere, conditionExtra, conditionArgs = m.formatCondition(ctx, false, false) + conditionStr = conditionWhere + conditionExtra ) + if m.unscoped { + fieldNameDelete = "" + } + if !gstr.ContainsI(conditionStr, " WHERE ") || (fieldNameDelete != "" && !gstr.ContainsI(conditionStr, " AND ")) { + intlog.Printf( + ctx, + `sql condition string "%s" has no WHERE for DELETE operation, fieldNameDelete: %s`, + conditionStr, fieldNameDelete, + ) + return nil, gerror.NewCode( + gcode.CodeMissingParameter, + "there should be WHERE condition statement for DELETE operation", + ) + } + // Soft deleting. - if !m.unscoped && fieldNameDelete != "" { + if fieldNameDelete != "" { in := &HookUpdateInput{ internalParamHookUpdate: internalParamHookUpdate{ internalParamHook: internalParamHook{ @@ -45,18 +62,11 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) { Model: m, Table: m.tables, Data: fmt.Sprintf(`%s=?`, m.db.GetCore().QuoteString(fieldNameDelete)), - Condition: conditionWhere + conditionExtra, + Condition: conditionStr, Args: append([]interface{}{gtime.Now().String()}, conditionArgs...), } return in.Next(ctx) } - conditionStr := conditionWhere + conditionExtra - if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.NewCode( - gcode.CodeMissingParameter, - "there should be WHERE condition statement for DELETE operation", - ) - } in := &HookDeleteInput{ internalParamHookDelete: internalParamHookDelete{ diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 10d1b1455bd..e27407c444b 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -617,7 +617,9 @@ func (m *Model) getFieldsFiltered() string { // Note that this function does not change any attribute value of the `m`. // // The parameter `limit1` specifies whether limits querying only one record if m.limit is not set. -func (m *Model) formatCondition(ctx context.Context, limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { +func (m *Model) formatCondition( + ctx context.Context, limit1 bool, isCountStatement bool, +) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) { var autoPrefix = m.getAutoPrefix() // GROUP BY. if m.groupBy != "" { diff --git a/database/gdb/gdb_model_update.go b/database/gdb/gdb_model_update.go index 3e27ba30e2d..5317bc73f18 100644 --- a/database/gdb/gdb_model_update.go +++ b/database/gdb/gdb_model_update.go @@ -9,6 +9,7 @@ package gdb import ( "database/sql" "fmt" + "github.com/gogf/gf/v2/internal/intlog" "reflect" "github.com/gogf/gf/v2/errors/gcode" @@ -48,7 +49,12 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro reflectInfo = reflection.OriginTypeAndKind(updateData) fieldNameUpdate = m.getSoftFieldNameUpdated("", m.tablesInit) conditionWhere, conditionExtra, conditionArgs = m.formatCondition(ctx, false, false) + conditionStr = conditionWhere + conditionExtra ) + if m.unscoped { + fieldNameUpdate = "" + } + switch reflectInfo.OriginKind { case reflect.Map, reflect.Struct: var dataMap map[string]interface{} @@ -57,7 +63,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro return nil, err } // Automatically update the record updating time. - if !m.unscoped && fieldNameUpdate != "" { + if fieldNameUpdate != "" { dataMap[fieldNameUpdate] = gtime.Now().String() } updateData = dataMap @@ -65,7 +71,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro default: updates := gconv.String(m.data) // Automatically update the record updating time. - if !m.unscoped && fieldNameUpdate != "" { + if fieldNameUpdate != "" { if fieldNameUpdate != "" && !gstr.Contains(updates, fieldNameUpdate) { updates += fmt.Sprintf(`,%s='%s'`, fieldNameUpdate, gtime.Now().String()) } @@ -76,9 +82,17 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro if err != nil { return nil, err } - conditionStr := conditionWhere + conditionExtra + if !gstr.ContainsI(conditionStr, " WHERE ") { - return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation") + intlog.Printf( + ctx, + `sql condition string "%s" has no WHERE for UPDATE operation, fieldNameUpdate: %s`, + conditionStr, fieldNameUpdate, + ) + return nil, gerror.NewCode( + gcode.CodeMissingParameter, + "there should be WHERE condition statement for UPDATE operation", + ) } in := &HookUpdateInput{ diff --git a/version.go b/version.go index 956a3369da9..0443f5247b2 100644 --- a/version.go +++ b/version.go @@ -2,5 +2,5 @@ package gf const ( // VERSION is the current GoFrame version. - VERSION = "v2.3.1" + VERSION = "v2.3.2" )