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

add field type detection for soft time field like created_at/updated_at/deleted_at to support unix timestamp or bool deleting table field #3293

Merged
merged 7 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
Loading