diff --git a/.example/database/gdb/mysql/gdb_batch_insert.go b/.example/database/gdb/mysql/gdb_batch_insert.go new file mode 100644 index 000000000..171bb9fb4 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_batch_insert.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/frame/g" +) + +func main() { + db := g.DB() + db.SetDebug(true) + list := make(g.List, 0) + for i := 0; i < 100; i++ { + list = append(list, g.Map{ + "name": fmt.Sprintf(`name_%d`, i), + }) + } + r, e := db.Table("user").Data(list).Batch(2).Insert() + if e != nil { + panic(e) + } + if r != nil { + fmt.Println(r.LastInsertId()) + } +} diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 12c0d25c1..5ccdc2516 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -690,7 +690,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i } } valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") - if len(values) == batchNum || (i == listMapLen-1 && len(values) > 0) { + if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) { r, err := c.DB.DoExec( link, fmt.Sprintf( diff --git a/database/gdb/gdb_driver_mysql.go b/database/gdb/gdb_driver_mysql.go index f5a7290ce..6831f5b27 100644 --- a/database/gdb/gdb_driver_mysql.go +++ b/database/gdb/gdb_driver_mysql.go @@ -42,7 +42,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) { } } else { source = fmt.Sprintf( - "%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true", + "%s:%s@tcp(%s:%s)/%s?charset=%s", config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, ) } diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 89d22e1b0..4eca381f3 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -164,6 +164,11 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { if m.batch > 0 { batch = m.batch } + newData, err := m.filterDataForInsertOrUpdate(list) + if err != nil { + return nil, err + } + list = newData.(List) // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { for k, v := range list { @@ -177,10 +182,6 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { list[k] = v } } - newData, err := m.filterDataForInsertOrUpdate(list) - if err != nil { - return nil, err - } return m.db.DoBatchInsert( m.getLink(true), m.tables, @@ -191,6 +192,11 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { } // Single operation. if data, ok := m.data.(Map); ok { + newData, err := m.filterDataForInsertOrUpdate(data) + if err != nil { + return nil, err + } + data = newData.(Map) // Automatic handling for creating/updating time. if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") { gutil.MapDelete(data, fieldNameCreate, fieldNameUpdate, fieldNameDelete) @@ -201,10 +207,6 @@ func (m *Model) doInsertWithOption(option int) (result sql.Result, err error) { data[fieldNameUpdate] = nowString } } - newData, err := m.filterDataForInsertOrUpdate(data) - if err != nil { - return nil, err - } return m.db.DoInsert( m.getLink(true), m.tables, diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 5da062227..9494ef514 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -8,11 +8,9 @@ package gdb import ( "database/sql" - "fmt" "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/gutil" "reflect" ) @@ -42,19 +40,19 @@ import ( // given parameter. // // See the example or unit testing cases for clear understanding for this function. -func (r Result) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) { +func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) { // Necessary checks for parameters. - if attributeName == "" { - return gerror.New(`attributeName should not be empty`) - } - if len(relation) > 0 { - if len(relation) < 2 { - return gerror.New(`relation name and key should are both necessary`) - } - if relation[0] == "" || relation[1] == "" { - return gerror.New(`relation name and key should not be empty`) - } + if bindToAttrName == "" { + return gerror.New(`bindToAttrName should not be empty`) } + //if len(relation) > 0 { + // if len(relation) < 2 { + // return gerror.New(`relation name and key should are both necessary`) + // } + // if relation[0] == "" || relation[1] == "" { + // return gerror.New(`relation name and key should not be empty`) + // } + //} var ( reflectValue = reflect.ValueOf(listPointer) @@ -65,12 +63,12 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation reflectKind = reflectValue.Kind() } if reflectKind != reflect.Ptr { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } reflectValue = reflectValue.Elem() reflectKind = reflectValue.Kind() if reflectKind != reflect.Slice && reflectKind != reflect.Array { - return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) + return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind) } length := len(r) if length == 0 { @@ -101,59 +99,61 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation // Relation variables. var ( - relationDataMap map[string]Value - relationFieldName string - relationAttrName string + relationKVStr string + relationDataMap map[string]Value + relationFromAttrName string // Eg: relationKV: User, uid:Uid -> User + relationResultFieldName string // Eg: relationKV: uid:Uid -> uid + relationBindToSubAttrName string // Eg: relationKV: uid:Uid -> Uid ) - if len(relation) > 0 { - array := gstr.Split(relation[1], ":") - if len(array) > 1 { + if len(relationKV) > 0 { + if len(relationKV) == 1 { + relationKVStr = relationKV[0] + } else { + relationFromAttrName = relationKV[0] + relationKVStr = relationKV[1] + } + array := gstr.SplitAndTrim(relationKVStr, ":") + if len(array) == 2 { // Defined table field to relation attribute name. // Like: // uid:Uid // uid:UserId - relationFieldName = array[0] - relationAttrName = array[1] + relationResultFieldName = array[0] + relationBindToSubAttrName = array[1] } else { - relationAttrName = relation[1] - // Find the possible map key by given only struct attribute name. - // Like: - // Uid - if k, _ := gutil.MapPossibleItemByKey(r[0].Map(), relation[1]); k != "" { - relationFieldName = k - } + return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`) } - if relationFieldName != "" { - relationDataMap = r.MapKeyValue(relationFieldName) + if relationResultFieldName != "" { + relationDataMap = r.MapKeyValue(relationResultFieldName) } if len(relationDataMap) == 0 { - return fmt.Errorf(`cannot find the relation data map, maybe invalid relation key given: %s`, relation[1]) + return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV) } } // Bind to target attribute. var ( - ok bool - attrValue reflect.Value - attrKind reflect.Kind - attrType reflect.Type - attrField reflect.StructField + ok bool + bindToAttrValue reflect.Value + bindToAttrKind reflect.Kind + bindToAttrType reflect.Type + bindToAttrField reflect.StructField ) if arrayItemType.Kind() == reflect.Ptr { - if attrField, ok = arrayItemType.Elem().FieldByName(attributeName); !ok { - return fmt.Errorf(`invalid field name: %s`, attributeName) + if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok { + return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) } } else { - if attrField, ok = arrayItemType.FieldByName(attributeName); !ok { - return fmt.Errorf(`invalid field name: %s`, attributeName) + if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok { + return gerror.Newf(`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`, bindToAttrName) } } - attrType = attrField.Type - attrKind = attrType.Kind() + bindToAttrType = bindToAttrField.Type + bindToAttrKind = bindToAttrType.Kind() // Bind to relation conditions. var ( - relationValue reflect.Value - relationField reflect.Value + relationFromAttrValue reflect.Value + relationFromAttrField reflect.Value ) for i := 0; i < arrayValue.Len(); i++ { arrayElemValue := arrayValue.Index(i) @@ -173,41 +173,45 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } else { // Like: []Entity } - attrValue = arrayElemValue.FieldByName(attributeName) - if len(relation) > 0 { - relationValue = arrayElemValue.FieldByName(relation[0]) - if relationValue.Kind() == reflect.Ptr { - relationValue = relationValue.Elem() + bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName) + if relationFromAttrName != "" { + // Attribute value of current slice element. + relationFromAttrValue = arrayElemValue.FieldByName(relationFromAttrName) + if relationFromAttrValue.Kind() == reflect.Ptr { + relationFromAttrValue = relationFromAttrValue.Elem() } + } else { + // Current slice element. + relationFromAttrValue = arrayElemValue } - if len(relationDataMap) > 0 && !relationValue.IsValid() { - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() { + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } - switch attrKind { + switch bindToAttrKind { case reflect.Array, reflect.Slice: if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { if err = gconv.Structs( - relationDataMap[gconv.String(relationField.Interface())], - attrValue.Addr(), + relationDataMap[gconv.String(relationFromAttrField.Interface())], + bindToAttrValue.Addr(), ); err != nil { return err } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { - return fmt.Errorf(`relationKey should not be empty as field "%s" is slice`, attributeName) + return gerror.Newf(`relationKey should not be empty as field "%s" is slice`, bindToAttrName) } case reflect.Ptr: - e := reflect.New(attrType.Elem()).Elem() + e := reflect.New(bindToAttrType.Elem()).Elem() if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { - v := relationDataMap[gconv.String(relationField.Interface())] + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { + v := relationDataMap[gconv.String(relationFromAttrField.Interface())] if v == nil { // There's no relational data. continue @@ -217,7 +221,7 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { v := r[i] @@ -229,14 +233,14 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation return err } } - attrValue.Set(e.Addr()) + bindToAttrValue.Set(e.Addr()) case reflect.Struct: - e := reflect.New(attrType).Elem() + e := reflect.New(bindToAttrType).Elem() if len(relationDataMap) > 0 { - relationField = relationValue.FieldByName(relationAttrName) - if relationField.IsValid() { - relationDataItem := relationDataMap[gconv.String(relationField.Interface())] + relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) + if relationFromAttrField.IsValid() { + relationDataItem := relationDataMap[gconv.String(relationFromAttrField.Interface())] if relationDataItem == nil { // There's no relational data. continue @@ -246,7 +250,7 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation } } else { // May be the attribute does not exist yet. - return fmt.Errorf(`invalid relation: "%s:%s"`, relation[0], relation[1]) + return gerror.Newf(`invalid relation specified: "%v"`, relationKV) } } else { relationDataItem := r[i] @@ -258,10 +262,10 @@ func (r Result) ScanList(listPointer interface{}, attributeName string, relation return err } } - attrValue.Set(e) + bindToAttrValue.Set(e) default: - return fmt.Errorf(`unsupport attribute type: %s`, attrKind.String()) + return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String()) } } reflect.ValueOf(listPointer).Elem().Set(arrayValue) diff --git a/database/gdb/gdb_z_mysql_association_test.go b/database/gdb/gdb_z_mysql_association_test.go index 856377fea..06b462017 100644 --- a/database/gdb/gdb_z_mysql_association_test.go +++ b/database/gdb/gdb_z_mysql_association_test.go @@ -472,3 +472,119 @@ CREATE TABLE %s ( t.Assert(users[1].UserScores[4].Score, 5) }) } + +func Test_Table_Relation_EmbedStruct(t *testing.T) { + var ( + tableUser = "user_" + gtime.TimestampMicroStr() + tableUserDetail = "user_detail_" + gtime.TimestampMicroStr() + tableUserScores = "user_scores_" + gtime.TimestampMicroStr() + ) + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + name varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUser)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUser) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + uid int(10) unsigned NOT NULL AUTO_INCREMENT, + address varchar(45) NOT NULL, + PRIMARY KEY (uid) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserDetail)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserDetail) + + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + uid int(10) unsigned NOT NULL, + score int(10) unsigned NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableUserScores)); err != nil { + gtest.Error(err) + } + defer dropTable(tableUserScores) + + type EntityUser struct { + Uid int `json:"uid"` + Name string `json:"name"` + } + type EntityUserDetail struct { + *EntityUser + Uid int `json:"uid"` + Address string `json:"address"` + } + type EntityUserScores struct { + *EntityUser + *EntityUserDetail + Id int `json:"id"` + Uid int `json:"uid"` + Score int `json:"score"` + } + + // Initialize the data. + var err error + for i := 1; i <= 5; i++ { + // User. + _, err = db.Insert(tableUser, g.Map{ + "uid": i, + "name": fmt.Sprintf(`name_%d`, i), + }) + gtest.Assert(err, nil) + // Detail. + _, err = db.Insert(tableUserDetail, g.Map{ + "uid": i, + "address": fmt.Sprintf(`address_%d`, i), + }) + gtest.Assert(err, nil) + // Scores. + for j := 1; j <= 5; j++ { + _, err = db.Insert(tableUserScores, g.Map{ + "uid": i, + "score": j, + }) + gtest.Assert(err, nil) + } + } + + gtest.C(t, func(t *gtest.T) { + var ( + err error + scores []*EntityUserScores + ) + // SELECT * FROM `user_scores` + err = db.Table(tableUserScores).Scan(&scores) + t.Assert(err, nil) + + // SELECT * FROM `user_scores` WHERE `uid` IN(1,2,3,4,5) + err = db.Table(tableUser). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUser", "uid:Uid") + t.Assert(err, nil) + + // SELECT * FROM `user_detail` WHERE `uid` IN(1,2,3,4,5) + err = db.Table(tableUserDetail). + Where("uid", gdb.ListItemValuesUnique(&scores, "Uid")). + ScanList(&scores, "EntityUserDetail", "uid:Uid") + t.Assert(err, nil) + + // Assertions. + t.Assert(len(scores), 25) + t.Assert(scores[0].Id, 1) + t.Assert(scores[0].Uid, 1) + t.Assert(scores[0].Name, "name_1") + t.Assert(scores[0].Address, "address_1") + t.Assert(scores[24].Id, 25) + t.Assert(scores[24].Uid, 5) + t.Assert(scores[24].Name, "name_5") + t.Assert(scores[24].Address, "address_5") + }) +} diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index b3e15d839..34efc5f22 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -17,8 +17,9 @@ import ( "github.com/gogf/gf/test/gtest" ) +// CreateAt/UpdateAt/DeleteAt func Test_SoftCreateUpdateDeleteTime(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -50,7 +51,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -72,7 +73,7 @@ CREATE TABLE %s ( t.Assert(oneSave["delete_at"].String(), "") t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) - t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // For time asserting purpose. time.Sleep(2 * time.Second) @@ -92,7 +93,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := g.Map{ @@ -127,7 +128,282 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Table(table).FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Table(table).Unscoped().Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(len(one6), 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + }) +} + +// CreatedAt/UpdatedAt/DeletedAt +func Test_SoftCreatedUpdatedDeletedTime_Map(t *testing.T) { + table := "time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(11) NOT NULL, + name varchar(45) DEFAULT NULL, + created_at datetime DEFAULT NULL, + updated_at datetime DEFAULT NULL, + deleted_at datetime DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := g.Map{ + "id": 1, + "name": "name_1", + } + r, err := db.Table(table).Data(dataInsert).Insert() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := g.Map{ + "id": 1, + "name": "name_10", + } + r, err = db.Table(table).Data(dataSave).Save() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneSave, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := g.Map{ + "name": "name_1000", + } + r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // Replace + dataReplace := g.Map{ + "id": 1, + "name": "name_100", + } + r, err = db.Table(table).Data(dataReplace).Replace() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneReplace, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneReplace["id"].Int(), 1) + t.Assert(oneReplace["name"].String(), "name_100") + t.Assert(oneReplace["deleted_at"].String(), "") + t.AssertGE(oneReplace["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneReplace["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Table(table).Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(len(one4), 0) + one5, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) + // Delete Count + i, err := db.Table(table).FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 1) + + // Delete Unscoped + r, err = db.Table(table).Unscoped().Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + one6, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(len(one6), 0) + i, err = db.Table(table).Unscoped().FindCount() + t.Assert(err, nil) + t.Assert(i, 0) + }) +} + +// CreatedAt/UpdatedAt/DeletedAt +func Test_SoftCreatedUpdatedDeletedTime_Struct(t *testing.T) { + table := "time_test_table_" + gtime.TimestampNanoStr() + if _, err := db.Exec(fmt.Sprintf(` +CREATE TABLE %s ( + id int(11) NOT NULL, + name varchar(45) DEFAULT NULL, + created_at datetime DEFAULT NULL, + updated_at datetime DEFAULT NULL, + deleted_at datetime DEFAULT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, table)); err != nil { + gtest.Error(err) + } + defer dropTable(table) + + type User struct { + Id int + Name string + CreatedAT *gtime.Time + UpdatedAT *gtime.Time + DeletedAT *gtime.Time + } + gtest.C(t, func(t *gtest.T) { + // Insert + dataInsert := User{ + Id: 1, + Name: "name_1", + } + r, err := db.Table(table).Data(dataInsert).Insert() + t.Assert(err, nil) + n, _ := r.RowsAffected() + t.Assert(n, 1) + + oneInsert, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneInsert["id"].Int(), 1) + t.Assert(oneInsert["name"].String(), "name_1") + t.Assert(oneInsert["deleted_at"].String(), "") + t.AssertGE(oneInsert["created_at"].GTime().Timestamp(), gtime.Timestamp()-2) + t.AssertGE(oneInsert["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Save + dataSave := User{ + Id: 1, + Name: "name_10", + } + r, err = db.Table(table).Data(dataSave).OmitEmpty().Save() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneSave, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneSave["id"].Int(), 1) + t.Assert(oneSave["name"].String(), "name_10") + t.Assert(oneSave["deleted_at"].String(), "") + t.Assert(oneSave["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertNE(oneSave["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + t.AssertGE(oneSave["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Update + dataUpdate := User{ + Name: "name_1000", + } + r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + + oneUpdate, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneUpdate["id"].Int(), 1) + t.Assert(oneUpdate["name"].String(), "name_1000") + t.Assert(oneUpdate["deleted_at"].String(), "") + t.Assert(oneUpdate["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneUpdate["updated_at"].GTime().Timestamp(), gtime.Timestamp()-2) + + // Replace + dataReplace := User{ + Id: 1, + Name: "name_100", + } + r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace() + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 2) + + oneReplace, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(oneReplace["id"].Int(), 1) + t.Assert(oneReplace["name"].String(), "name_100") + t.Assert(oneReplace["deleted_at"].String(), "") + t.AssertGE(oneReplace["created_at"].GTime().Timestamp(), oneInsert["created_at"].GTime().Timestamp()) + t.AssertGE(oneReplace["updated_at"].GTime().Timestamp(), oneInsert["updated_at"].GTime().Timestamp()) + + // For time asserting purpose. + time.Sleep(2 * time.Second) + + // Delete + r, err = db.Table(table).Delete("id", 1) + t.Assert(err, nil) + n, _ = r.RowsAffected() + t.Assert(n, 1) + // Delete Select + one4, err := db.Table(table).FindOne(1) + t.Assert(err, nil) + t.Assert(len(one4), 0) + one5, err := db.Table(table).Unscoped().FindOne(1) + t.Assert(err, nil) + t.Assert(one5["id"].Int(), 1) + t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) @@ -372,7 +648,7 @@ CREATE TABLE %s ( t.Assert(oneInsert["name"].String(), "name_1") t.Assert(oneInsert["delete_at"].String(), "") t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2) - t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()) + t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) time.Sleep(2 * time.Second) @@ -396,7 +672,7 @@ CREATE TABLE %s ( t.Assert(oneSave["delete_at"].String(), "") t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp()) - t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) time.Sleep(2 * time.Second) @@ -419,7 +695,7 @@ CREATE TABLE %s ( t.Assert(oneUpdate["name"].String(), "name_1000") t.Assert(oneUpdate["delete_at"].String(), "") t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp()) - t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Replace dataReplace := &Entity{ @@ -456,7 +732,7 @@ CREATE TABLE %s ( one5, err := db.Table(table).Unscoped().FindOne(1) t.Assert(err, nil) t.Assert(one5["id"].Int(), 1) - t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2) + t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2) // Delete Count i, err := db.Table(table).FindCount() t.Assert(err, nil) diff --git a/os/gcron/gcron.go b/os/gcron/gcron.go index 8abe47423..7f45c4b2c 100644 --- a/os/gcron/gcron.go +++ b/os/gcron/gcron.go @@ -15,11 +15,11 @@ import ( ) const ( - StatusReady = gtimer.StatusReady - StatusRunning = gtimer.StatusRunning - StatusStopped = gtimer.StatusStopped - StatusClosed = gtimer.StatusClosed - gDEFAULT_TIMES = math.MaxInt32 + StatusReady = gtimer.StatusReady + StatusRunning = gtimer.StatusRunning + StatusStopped = gtimer.StatusStopped + StatusClosed = gtimer.StatusClosed + defaultTimes = math.MaxInt32 ) var ( diff --git a/os/gcron/gcron_entry.go b/os/gcron/gcron_entry.go index 9b9087639..94ff02f43 100644 --- a/os/gcron/gcron_entry.go +++ b/os/gcron/gcron_entry.go @@ -43,7 +43,7 @@ func (c *Cron) addEntry(pattern string, job func(), singleton bool, name ...stri cron: c, schedule: schedule, jobName: runtime.FuncForPC(reflect.ValueOf(job).Pointer()).Name(), - times: gtype.NewInt(gDEFAULT_TIMES), + times: gtype.NewInt(defaultTimes), Job: job, Time: time.Now(), } @@ -130,7 +130,7 @@ func (entry *Entry) check() { } } if times < 2000000000 && times > 1000000000 { - entry.times.Set(gDEFAULT_TIMES) + entry.times.Set(defaultTimes) } glog.Path(path).Level(level).Debugf("[gcron] %s(%s) %s start", entry.Name, entry.schedule.pattern, entry.jobName) defer func() { diff --git a/os/gcron/gcron_unit_1_test.go b/os/gcron/gcron_unit_1_test.go index 0807f4f4f..c8754498b 100644 --- a/os/gcron/gcron_unit_1_test.go +++ b/os/gcron/gcron_unit_1_test.go @@ -7,7 +7,7 @@ package gcron_test import ( - "github.com/gogf/gf/os/glog" + "github.com/gogf/gf/frame/g" "testing" "time" @@ -21,34 +21,24 @@ func TestCron_Add_Close(t *testing.T) { cron := gcron.New() array := garray.New(true) _, err1 := cron.Add("* * * * * *", func() { - glog.Println("cron1") + g.Log().Println("cron1") array.Append(1) }) _, err2 := cron.Add("* * * * * *", func() { - glog.Println("cron2") + g.Log().Println("cron2") array.Append(1) }, "test") - _, err3 := cron.Add("* * * * * *", func() { - glog.Println("cron3") - array.Append(1) - }, "test") - _, err4 := cron.Add("@every 2s", func() { - glog.Println("cron4") - array.Append(1) - }) t.Assert(err1, nil) t.Assert(err2, nil) - t.AssertNE(err3, nil) - t.Assert(err4, nil) - t.Assert(cron.Size(), 3) + t.Assert(cron.Size(), 2) time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 2) - time.Sleep(1400 * time.Millisecond) - t.Assert(array.Len(), 5) + time.Sleep(1300 * time.Millisecond) + t.Assert(array.Len(), 4) cron.Close() - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) fixedLength := array.Len() - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), fixedLength) }) } diff --git a/os/gcron/gcron_unit_2_test.go b/os/gcron/gcron_unit_2_test.go index fc157a929..5956dc8bd 100644 --- a/os/gcron/gcron_unit_2_test.go +++ b/os/gcron/gcron_unit_2_test.go @@ -7,21 +7,23 @@ package gcron_test import ( + "github.com/gogf/gf/frame/g" "testing" "time" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/os/gcron" - "github.com/gogf/gf/os/glog" "github.com/gogf/gf/test/gtest" ) func TestCron_Entry_Operations(t *testing.T) { gtest.C(t, func(t *gtest.T) { - cron := gcron.New() - array := garray.New(true) + var ( + cron = gcron.New() + array = garray.New(true) + ) cron.DelayAddTimes(500*time.Millisecond, "* * * * * *", 2, func() { - glog.Println("add times") + g.Log().Println("add times") array.Append(1) }) t.Assert(cron.Size(), 0) @@ -34,16 +36,18 @@ func TestCron_Entry_Operations(t *testing.T) { }) gtest.C(t, func(t *gtest.T) { - cron := gcron.New() - array := garray.New(true) + var ( + cron = gcron.New() + array = garray.New(true) + ) entry, err1 := cron.Add("* * * * * *", func() { - glog.Println("add") + g.Log().Println("add") array.Append(1) }) t.Assert(err1, nil) t.Assert(array.Len(), 0) t.Assert(cron.Size(), 1) - time.Sleep(1200 * time.Millisecond) + time.Sleep(1300 * time.Millisecond) t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Stop() @@ -51,8 +55,8 @@ func TestCron_Entry_Operations(t *testing.T) { t.Assert(array.Len(), 1) t.Assert(cron.Size(), 1) entry.Start() - glog.Println("start") - time.Sleep(1200 * time.Millisecond) + g.Log().Println("start") + time.Sleep(1000 * time.Millisecond) t.Assert(array.Len(), 2) t.Assert(cron.Size(), 1) entry.Close() diff --git a/os/gtimer/gtimer_z_unit_timer_internal_test.go b/os/gtimer/gtimer_z_unit_timer_internal_test.go index 64c4ab6c7..3953cf4d3 100644 --- a/os/gtimer/gtimer_z_unit_timer_internal_test.go +++ b/os/gtimer/gtimer_z_unit_timer_internal_test.go @@ -39,20 +39,11 @@ func TestTimer_Proceed(t *testing.T) { timer.AddOnce(2*time.Hour, func() { slice = append(slice, 6) }) - timer.AddOnce(1000*time.Minute, func() { - slice = append(slice, 7) - }) - timer.AddOnce(1100*time.Minute, func() { - slice = append(slice, 8) - }) - timer.AddOnce(1200*time.Minute, func() { - slice = append(slice, 9) - }) - for i := 0; i < 2000000; i++ { + for i := 0; i < 500000; i++ { timer.wheels[0].proceed() time.Sleep(10 * time.Microsecond) } time.Sleep(time.Second) - t.Assert(slice, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}) + t.Assert(slice, []int{1, 2, 3, 4, 5, 6}) }) }