Skip to content

Commit

Permalink
add field type detection for soft time field like `created_at/updated…
Browse files Browse the repository at this point in the history
…_at/deleted_at` to support unix timestamp or bool deleting table field (#3293)
  • Loading branch information
gqcn committed Feb 6, 2024
1 parent 85c5b7f commit 2acdf4b
Show file tree
Hide file tree
Showing 11 changed files with 791 additions and 248 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
"testing"
"time"

"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)

// CreateAt/UpdateAt/DeleteAt.
func Test_SoftCreateUpdateDeleteTimeMicroSecond(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
func Test_SoftTime_CreateUpdateDelete1(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -151,8 +152,8 @@ CREATE TABLE %s (
}

// CreateAt/UpdateAt/DeleteAt.
func Test_SoftCreateUpdateDeleteTimeSecond(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
func Test_SoftTime_CreateUpdateDelete2(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -285,8 +286,8 @@ CREATE TABLE %s (
}

// CreatedAt/UpdatedAt/DeletedAt.
func Test_SoftCreatedUpdatedDeletedTime_Map(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
func Test_SoftTime_CreatedUpdatedDeleted_Map(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -419,8 +420,8 @@ CREATE TABLE %s (
}

// CreatedAt/UpdatedAt/DeletedAt.
func Test_SoftCreatedUpdatedDeletedTime_Struct(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
func Test_SoftTime_CreatedUpdatedDeleted_Struct(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -560,7 +561,7 @@ CREATE TABLE %s (
}

func Test_SoftUpdateTime(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -600,7 +601,7 @@ CREATE TABLE %s (
}

func Test_SoftUpdateTime_WithDO(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -657,7 +658,7 @@ CREATE TABLE %s (
}

func Test_SoftDelete(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -796,7 +797,7 @@ CREATE TABLE %s (
}

func Test_SoftDelete_WhereAndOr(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -838,7 +839,7 @@ CREATE TABLE %s (
}

func Test_CreateUpdateTime_Struct(t *testing.T) {
table := "time_test_table_" + gtime.TimestampNanoStr()
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
Expand Down Expand Up @@ -989,3 +990,280 @@ CREATE TABLE %s (
t.Assert(i, 0)
})
}

func Test_SoftTime_CreateUpdateDelete_UnixTimestamp(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at int(11) DEFAULT NULL,
update_at int(11) DEFAULT NULL,
delete_at int(11) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)

// insert
gtest.C(t, func(t *gtest.T) {
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Model(table).Data(dataInsert).Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 0)
t.Assert(len(one["create_at"].String()), 10)
t.Assert(len(one["update_at"].String()), 10)
})

// sleep some seconds to make update time greater than create time.
time.Sleep(2 * time.Second)

// update
gtest.C(t, func(t *gtest.T) {
// update: map
dataInsert := g.Map{
"name": "name_11",
}
r, err := db.Model(table).Data(dataInsert).WherePri(1).Update()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_11")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 0)
t.Assert(len(one["create_at"].String()), 10)
t.Assert(len(one["update_at"].String()), 10)

var (
lastCreateTime = one["create_at"].Int64()
lastUpdateTime = one["update_at"].Int64()
)

time.Sleep(2 * time.Second)

// update: string
r, err = db.Model(table).Data("name='name_111'").WherePri(1).Update()
t.AssertNil(err)
n, _ = r.RowsAffected()
t.Assert(n, 1)

one, err = db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_111")
t.Assert(one["create_at"].Int64(), lastCreateTime)
t.AssertGT(one["update_at"].Int64(), lastUpdateTime)
t.Assert(one["delete_at"].Int64(), 0)
})

// delete
gtest.C(t, func(t *gtest.T) {
r, err := db.Model(table).WherePri(1).Delete()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(len(one), 0)

one, err = db.Model(table).Unscoped().WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_111")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.AssertGT(one["delete_at"].Int64(), 0)
})
}

func Test_SoftTime_CreateUpdateDelete_Bool_Deleted(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at int(11) DEFAULT NULL,
update_at int(11) DEFAULT NULL,
delete_at bit(1) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)

//db.SetDebug(true)
// insert
gtest.C(t, func(t *gtest.T) {
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Model(table).Data(dataInsert).Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 0)
t.Assert(len(one["create_at"].String()), 10)
t.Assert(len(one["update_at"].String()), 10)
})

// delete
gtest.C(t, func(t *gtest.T) {
r, err := db.Model(table).WherePri(1).Delete()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).WherePri(1).One()
t.AssertNil(err)
t.Assert(len(one), 0)

one, err = db.Model(table).Unscoped().WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 1)
})
}

func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampMilli(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at bigint(19) unsigned DEFAULT NULL,
update_at bigint(19) unsigned DEFAULT NULL,
delete_at bit(1) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)

var softTimeOption = gdb.SoftTimeOption{
SoftTimeType: gdb.SoftTimeTypeTimestampMilli,
}

// insert
gtest.C(t, func(t *gtest.T) {
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.Assert(len(one["create_at"].String()), 13)
t.Assert(len(one["update_at"].String()), 13)
t.Assert(one["delete_at"].Int64(), 0)
})

// delete
gtest.C(t, func(t *gtest.T) {
r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
t.AssertNil(err)
t.Assert(len(one), 0)

one, err = db.Model(table).Unscoped().WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 1)
})
}

func Test_SoftTime_CreateUpdateDelete_Option_SoftTimeTypeTimestampNano(t *testing.T) {
table := "soft_time_test_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at bigint(19) unsigned DEFAULT NULL,
update_at bigint(19) unsigned DEFAULT NULL,
delete_at bit(1) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)

var softTimeOption = gdb.SoftTimeOption{
SoftTimeType: gdb.SoftTimeTypeTimestampNano,
}

// insert
gtest.C(t, func(t *gtest.T) {
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Model(table).SoftTime(softTimeOption).Data(dataInsert).Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.Assert(len(one["create_at"].String()), 19)
t.Assert(len(one["update_at"].String()), 19)
t.Assert(one["delete_at"].Int64(), 0)
})

// delete
gtest.C(t, func(t *gtest.T) {
r, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).Delete()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)

one, err := db.Model(table).SoftTime(softTimeOption).WherePri(1).One()
t.AssertNil(err)
t.Assert(len(one), 0)

one, err = db.Model(table).Unscoped().WherePri(1).One()
t.AssertNil(err)
t.Assert(one["name"].String(), "name_1")
t.AssertGT(one["create_at"].Int64(), 0)
t.AssertGT(one["update_at"].Int64(), 0)
t.Assert(one["delete_at"].Int64(), 1)
})
}
1 change: 1 addition & 0 deletions database/gdb/gdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ const (
type LocalType string

const (
LocalTypeUndefined LocalType = ""
LocalTypeString LocalType = "string"
LocalTypeDate LocalType = "date"
LocalTypeDatetime LocalType = "datetime"
Expand Down
4 changes: 3 additions & 1 deletion database/gdb/gdb_core_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,9 @@ func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fie
// ConvertValueForLocal converts value to local Golang type of value according field type name from database.
// The parameter `fieldType` is in lower case, like:
// `float(5,2)`, `unsigned double(5,2)`, `decimal(10,2)`, `char(45)`, `varchar(100)`, etc.
func (c *Core) ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) {
func (c *Core) ConvertValueForLocal(
ctx context.Context, fieldType string, fieldValue interface{},
) (interface{}, error) {
// If there's no type retrieved, it returns the `fieldValue` directly
// to use its original data type, as `fieldValue` is type of interface{}.
if fieldType == "" {
Expand Down

0 comments on commit 2acdf4b

Please sign in to comment.