Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(database/gdb): add time field type for value converting for/from field #3712

Merged
merged 10 commits into from
Sep 10, 2024
2 changes: 1 addition & 1 deletion cmd/gf/internal/cmd/gendao/gendao_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func generateStructFieldDefinition(
}
localTypeNameStr = string(localTypeName)
switch localTypeName {
case gdb.LocalTypeDate, gdb.LocalTypeDatetime:
case gdb.LocalTypeDate, gdb.LocalTypeTime, gdb.LocalTypeDatetime:
if in.StdTime {
localTypeNameStr = "time.Time"
} else {
Expand Down
33 changes: 23 additions & 10 deletions contrib/drivers/mysql/mysql_z_unit_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func Test_DB_Insert(t *testing.T) {
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T1",
"create_time": gtime.Now().String(),
"create_date": gtime.Date(),
})
t.AssertNil(err)

Expand All @@ -114,26 +115,30 @@ func Test_DB_Insert(t *testing.T) {
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "name_2",
"create_time": gtime.Now().String(),
"create_date": gtime.Date(),
})
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)

// struct
type User struct {
Id int `gconv:"id"`
Passport string `json:"passport"`
Password string `gconv:"password"`
Nickname string `gconv:"nickname"`
CreateTime string `json:"create_time"`
}
timeStr := gtime.New("2024-10-01 12:01:01").String()
Id int `gconv:"id"`
Passport string `json:"passport"`
Password string `gconv:"password"`
Nickname string `gconv:"nickname"`
CreateTime string `json:"create_time"`
CreateDate *gtime.Time `json:"create_date"`
}
gTime := gtime.New("2024-10-01 12:01:01")
timeStr, dateStr := gTime.String(), "2024-10-01 00:00:00"
result, err = db.Insert(ctx, table, User{
Id: 3,
Passport: "user_3",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "name_3",
CreateTime: timeStr,
CreateDate: gTime,
})
t.AssertNil(err)
n, _ = result.RowsAffected()
Expand All @@ -147,15 +152,18 @@ func Test_DB_Insert(t *testing.T) {
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
t.Assert(one["nickname"].String(), "name_3")
t.Assert(one["create_time"].GTime().String(), timeStr)
t.Assert(one["create_date"].GTime().String(), dateStr)

// *struct
timeStr = gtime.New("2024-10-01 12:01:01").String()
gTime = gtime.New("2024-10-01 12:01:01")
timeStr, dateStr = gTime.String(), "2024-10-01 00:00:00"
result, err = db.Insert(ctx, table, &User{
Id: 4,
Passport: "t4",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "name_4",
CreateTime: timeStr,
CreateDate: gTime,
})
t.AssertNil(err)
n, _ = result.RowsAffected()
Expand All @@ -168,23 +176,27 @@ func Test_DB_Insert(t *testing.T) {
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
t.Assert(one["nickname"].String(), "name_4")
t.Assert(one["create_time"].GTime().String(), timeStr)
t.Assert(one["create_date"].GTime().String(), dateStr)

// batch with Insert
timeStr = gtime.New("2024-10-01 12:01:01").String()
gTime = gtime.New("2024-10-01 12:01:01")
timeStr, dateStr = gTime.String(), "2024-10-01 00:00:00"
r, err := db.Insert(ctx, table, g.Slice{
g.Map{
"id": 200,
"passport": "t200",
"password": "25d55ad283aa400af464c76d71qw07ad",
"nickname": "T200",
"create_time": timeStr,
"create_date": gTime,
},
g.Map{
"id": 300,
"passport": "t300",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T300",
"create_time": timeStr,
"create_date": gTime,
},
})
t.AssertNil(err)
Expand All @@ -198,6 +210,7 @@ func Test_DB_Insert(t *testing.T) {
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d71qw07ad")
t.Assert(one["nickname"].String(), "T200")
t.Assert(one["create_time"].GTime().String(), timeStr)
t.Assert(one["create_date"].GTime().String(), dateStr)
})
}

Expand Down Expand Up @@ -1644,7 +1657,7 @@ func Test_Core_ClearTableFields(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
fields, err := db.TableFields(ctx, table)
t.AssertNil(err)
t.Assert(len(fields), 5)
t.Assert(len(fields), 6)
})
gtest.C(t, func(t *gtest.T) {
err := db.GetCore().ClearTableFields(ctx, table)
Expand Down
1 change: 1 addition & 0 deletions contrib/drivers/mysql/mysql_z_unit_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
password char(32) NULL,
nickname varchar(45) NULL,
create_time timestamp(6) NULL,
create_date date NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, name,
Expand Down
8 changes: 4 additions & 4 deletions contrib/drivers/mysql/mysql_z_unit_issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ func Test_Issue2552_ClearTableFieldsAll(t *testing.T) {
ctx = context.Background()
sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error {
one, err := db.Model(table).Ctx(ctx).One()
t.Assert(len(one), 5)
t.Assert(len(one), 6)
return err
})
t.AssertNil(err)
Expand All @@ -1078,7 +1078,7 @@ func Test_Issue2552_ClearTableFieldsAll(t *testing.T) {
ctx = context.Background()
sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error {
one, err := db.Model(table).Ctx(ctx).One()
t.Assert(len(one), 4)
t.Assert(len(one), 5)
return err
})
t.AssertNil(err)
Expand Down Expand Up @@ -1109,7 +1109,7 @@ func Test_Issue2552_ClearTableFields(t *testing.T) {
ctx = context.Background()
sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error {
one, err := db.Model(table).Ctx(ctx).One()
t.Assert(len(one), 5)
t.Assert(len(one), 6)
return err
})
t.AssertNil(err)
Expand All @@ -1124,7 +1124,7 @@ func Test_Issue2552_ClearTableFields(t *testing.T) {
ctx = context.Background()
sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error {
one, err := db.Model(table).Ctx(ctx).One()
t.Assert(len(one), 4)
t.Assert(len(one), 5)
return err
})
t.AssertNil(err)
Expand Down
19 changes: 10 additions & 9 deletions contrib/drivers/mysql/mysql_z_unit_model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2335,7 +2335,7 @@ func Test_Model_FieldsEx(t *testing.T) {
r, err := db.Model(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All()
t.AssertNil(err)
t.Assert(len(r), 2)
t.Assert(len(r[0]), 3)
t.Assert(len(r[0]), 4)
t.Assert(r[0]["id"], "")
t.Assert(r[0]["passport"], "user_1")
t.Assert(r[0]["password"], "pass_1")
Expand Down Expand Up @@ -2982,7 +2982,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) {
"CreateTime": 1,
}).Where("id", 2).One()
t.AssertNil(err)
t.Assert(len(one), 2)
t.Assert(len(one), 3)
t.Assert(one["id"], 2)
t.Assert(one["nickname"], "name_2")
})
Expand All @@ -2999,7 +2999,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) {
CreateTime: 0,
}).Where("id", 2).One()
t.AssertNil(err)
t.Assert(len(one), 2)
t.Assert(len(one), 3)
t.Assert(one["id"], 2)
t.Assert(one["nickname"], "name_2")
})
Expand Down Expand Up @@ -3157,8 +3157,8 @@ func Test_TimeZoneInsert(t *testing.T) {
gtest.AssertNil(err)

CreateTime := "2020-11-22 12:23:45"
UpdateTime := "2020-11-22 13:23:45"
DeleteTime := "2020-11-22 14:23:45"
UpdateTime := "2020-11-22 13:23:46"
DeleteTime := "2020-11-22 14:23:47"
type User struct {
Id int `json:"id"`
CreatedAt *gtime.Time `json:"created_at"`
Expand All @@ -3176,13 +3176,14 @@ func Test_TimeZoneInsert(t *testing.T) {
}

gtest.C(t, func(t *gtest.T) {
_, _ = db.Model(tableName).Unscoped().Insert(u)
_, err = db.Model(tableName).Unscoped().Insert(u)
t.AssertNil(err)
userEntity := &User{}
err := db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity)
err = db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity)
t.AssertNil(err)
t.Assert(userEntity.CreatedAt.String(), "2020-11-22 11:23:45")
t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 12:23:45")
t.Assert(gtime.NewFromTime(userEntity.DeletedAt).String(), "2020-11-22 13:23:45")
t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 12:23:46")
t.Assert(gtime.NewFromTime(userEntity.DeletedAt).String(), "2020-11-22 13:23:47")
})
}

Expand Down
2 changes: 2 additions & 0 deletions database/gdb/gdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ type LocalType string
const (
LocalTypeUndefined LocalType = ""
LocalTypeString LocalType = "string"
LocalTypeTime LocalType = "time"
LocalTypeDate LocalType = "date"
LocalTypeDatetime LocalType = "datetime"
LocalTypeInt LocalType = "int"
Expand Down Expand Up @@ -492,6 +493,7 @@ const (
fieldTypeBool = "bool"
fieldTypeBit = "bit"
fieldTypeDate = "date"
fieldTypeTime = "time"
fieldTypeDatetime = "datetime"
fieldTypeTimestamp = "timestamp"
fieldTypeTimestampz = "timestamptz"
Expand Down
46 changes: 41 additions & 5 deletions database/gdb/gdb_core_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,18 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field
err error
convertedValue = fieldValue
)
switch fieldValue.(type) {
case time.Time, *time.Time, gtime.Time, *gtime.Time:
goto Default
}
// If `value` implements interface `driver.Valuer`, it then uses the interface for value converting.
if valuer, ok := fieldValue.(driver.Valuer); ok {
if convertedValue, err = valuer.Value(); err != nil {
return nil, err
}
return convertedValue, nil
}
Default:
// Default value converting.
var (
rvValue = reflect.ValueOf(fieldValue)
Expand All @@ -100,6 +105,9 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field
rvKind = rvValue.Kind()
}
switch rvKind {
case reflect.Invalid:
convertedValue = nil

case reflect.Slice, reflect.Array, reflect.Map:
// It should ignore the bytes type.
if _, ok := fieldValue.([]byte); !ok {
Expand All @@ -109,33 +117,50 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field
return nil, err
}
}

case reflect.Struct:
switch r := fieldValue.(type) {
// If the time is zero, it then updates it to nil,
// which will insert/update the value to database as "null".
case time.Time:
if r.IsZero() {
convertedValue = nil
} else if fieldType == fieldTypeDate {
convertedValue = r.Format("2006-01-02")
} else if fieldType == fieldTypeTime {
convertedValue = r.Format("15:04:05")
}

case *time.Time:
if r == nil {
// Nothing to do.
} else if fieldType == fieldTypeDate {
convertedValue = r.Format("2006-01-02")
} else if fieldType == fieldTypeTime {
convertedValue = r.Format("15:04:05")
}

case gtime.Time:
if r.IsZero() {
convertedValue = nil
} else if fieldType == fieldTypeDate {
convertedValue = r.Layout("2006-01-02")
} else if fieldType == fieldTypeTime {
convertedValue = r.Layout("15:04:05")
} else {
convertedValue = r.Time
}

case *gtime.Time:
if r.IsZero() {
convertedValue = nil
} else if fieldType == fieldTypeDate {
convertedValue = r.Layout("2006-01-02")
} else if fieldType == fieldTypeTime {
convertedValue = r.Layout("15:04:05")
} else {
convertedValue = r.Time
}

case *time.Time:
// Nothing to do.

case Counter, *Counter:
// Nothing to do.

Expand All @@ -157,6 +182,7 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field
}
}
}

return convertedValue, nil
}

Expand Down Expand Up @@ -247,6 +273,10 @@ func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fie
fieldTypeDate:
return LocalTypeDate, nil

case
fieldTypeTime:
return LocalTypeTime, nil

case
fieldTypeDatetime,
fieldTypeTimestamp,
Expand Down Expand Up @@ -353,13 +383,19 @@ func (c *Core) ConvertValueForLocal(
return gconv.Bool(fieldValue), nil

case LocalTypeDate:
// Date without time.
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("Y-m-d"), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("Y-m-d"), nil

case LocalTypeTime:
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("H:i:s"), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("H:i:s"), nil

case LocalTypeDatetime:
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t), nil
Expand Down
6 changes: 3 additions & 3 deletions database/gdb/gdb_model_soft_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func (m *softTimeMaintainer) getConditionByFieldNameAndTypeForSoftDeleting(
switch m.softTimeOption.SoftTimeType {
case SoftTimeTypeAuto:
switch fieldType {
case LocalTypeDate, LocalTypeDatetime:
case LocalTypeDate, LocalTypeTime, LocalTypeDatetime:
return fmt.Sprintf(`%s IS NULL`, quotedFieldName)
case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64, LocalTypeBool:
return fmt.Sprintf(`%s=0`, quotedFieldName)
Expand Down Expand Up @@ -368,7 +368,7 @@ func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate(
var value any
if isDeletedField {
switch fieldType {
case LocalTypeDate, LocalTypeDatetime:
case LocalTypeDate, LocalTypeTime, LocalTypeDatetime:
value = nil
default:
value = 0
Expand All @@ -378,7 +378,7 @@ func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate(
switch m.softTimeOption.SoftTimeType {
case SoftTimeTypeAuto:
switch fieldType {
case LocalTypeDate, LocalTypeDatetime:
case LocalTypeDate, LocalTypeTime, LocalTypeDatetime:
value = gtime.Now()
case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64:
value = gtime.Timestamp()
Expand Down