From 3f2b1cb32955a8e2175101d7d0ac41b9d8d30c3e Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 25 Sep 2024 16:33:52 +0800 Subject: [PATCH] feat(database/gdb): add year field type support for ORM operations (#3805) --- .github/workflows/ci-main.yml | 5 ++ cmd/gf/internal/cmd/cmd.go | 2 +- .../drivers/mysql/mysql_z_unit_model_test.go | 32 ++++++++ .../mysql/testdata/date_time_example.sql | 9 +++ database/gdb/gdb.go | 9 ++- database/gdb/gdb_core_structure.go | 76 ++++++++++++++----- 6 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 contrib/drivers/mysql/testdata/date_time_example.sql diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index c3b1a24de61..37af558ccc1 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -58,6 +58,11 @@ jobs: - 6379:6379 # MySQL backend server. + # docker run -d --name mysql \ + # -p 3306:3306 \ + # -e MYSQL_DATABASE=test \ + # -e MYSQL_ROOT_PASSWORD=12345678 \ + # loads/mysql:5.7 mysql: image: loads/mysql:5.7 env: diff --git a/cmd/gf/internal/cmd/cmd.go b/cmd/gf/internal/cmd/cmd.go index bd5e8922e07..4a39fd17b9a 100644 --- a/cmd/gf/internal/cmd/cmd.go +++ b/cmd/gf/internal/cmd/cmd.go @@ -59,7 +59,7 @@ func (c cGF) Index(ctx context.Context, in cGFInput) (out *cGFOutput, err error) answer := "n" // No argument or option, do installation checks. if data, isInstalled := service.Install.IsInstalled(); !isInstalled { - mlog.Print("hi, it seams it's the first time you installing gf cli.") + mlog.Print("hi, it seems it's the first time you installing gf cli.") answer = gcmd.Scanf("do you want to install gf(%s) binary to your system? [y/n]: ", gf.VERSION) } else if !data.IsSelf { mlog.Print("hi, you have installed gf cli.") diff --git a/contrib/drivers/mysql/mysql_z_unit_model_test.go b/contrib/drivers/mysql/mysql_z_unit_model_test.go index 02532d42b2c..c2b5b5fb33d 100644 --- a/contrib/drivers/mysql/mysql_z_unit_model_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_model_test.go @@ -4786,3 +4786,35 @@ func Test_Model_FixGdbJoin(t *testing.T) { t.Assert(gtest.DataContent(`fix_gdb_join_expect.sql`), sqlSlice[len(sqlSlice)-1]) }) } + +func Test_Model_Year_Date_Time_DateTime_Timestamp(t *testing.T) { + table := "date_time_example" + array := gstr.SplitAndTrim(gtest.DataContent(`date_time_example.sql`), ";") + for _, v := range array { + if _, err := db.Exec(ctx, v); err != nil { + gtest.Error(err) + } + } + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + // insert. + var now = gtime.Now() + _, err := db.Model("date_time_example").Insert(g.Map{ + "year": now, + "date": now, + "time": now, + "datetime": now, + "timestamp": now, + }) + t.AssertNil(err) + // select. + one, err := db.Model("date_time_example").One() + t.AssertNil(err) + t.Assert(one["year"].String(), now.Format("Y")) + t.Assert(one["date"].String(), now.Format("Y-m-d")) + t.Assert(one["time"].String(), now.Format("H:i:s")) + t.AssertLT(one["datetime"].GTime().Sub(now).Seconds(), 5) + t.AssertLT(one["timestamp"].GTime().Sub(now).Seconds(), 5) + }) +} diff --git a/contrib/drivers/mysql/testdata/date_time_example.sql b/contrib/drivers/mysql/testdata/date_time_example.sql new file mode 100644 index 00000000000..4dc0568a915 --- /dev/null +++ b/contrib/drivers/mysql/testdata/date_time_example.sql @@ -0,0 +1,9 @@ +CREATE TABLE `date_time_example` ( + `id` int unsigned NOT NULL AUTO_INCREMENT, + `year` year DEFAULT NULL COMMENT 'year', + `date` date DEFAULT NULL COMMENT 'Date', + `time` time DEFAULT NULL COMMENT 'time', + `datetime` datetime DEFAULT NULL COMMENT 'datetime', + `timestamp` timestamp NULL DEFAULT NULL COMMENT 'Timestamp', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 490edf8fc9f..c1f86d65ef4 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -493,10 +493,11 @@ const ( fieldTypeSmallmoney = "smallmoney" fieldTypeBool = "bool" fieldTypeBit = "bit" - fieldTypeDate = "date" - fieldTypeTime = "time" - fieldTypeDatetime = "datetime" - fieldTypeTimestamp = "timestamp" + fieldTypeYear = "year" // YYYY + fieldTypeDate = "date" // YYYY-MM-DD + fieldTypeTime = "time" // HH:MM:SS + fieldTypeDatetime = "datetime" // YYYY-MM-DD HH:MM:SS + fieldTypeTimestamp = "timestamp" // YYYYMMDD HHMMSS fieldTypeTimestampz = "timestamptz" fieldTypeJson = "json" fieldTypeJsonb = "jsonb" diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index e491d1def18..bbb9df7574c 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -28,7 +28,19 @@ import ( func (c *Core) GetFieldTypeStr(ctx context.Context, fieldName, table, schema string) string { field := c.GetFieldType(ctx, fieldName, table, schema) if field != nil { - return field.Type + // Kinds of data type examples: + // year(4) + // datetime + // varchar(64) + // bigint(20) + // int(10) unsigned + typeName := gstr.StrTillEx(field.Type, "(") // int(10) unsigned -> int + if typeName != "" { + typeName = gstr.Trim(typeName) + } else { + typeName = field.Type + } + return typeName } return "" } @@ -63,9 +75,10 @@ func (c *Core) ConvertDataForRecord(ctx context.Context, value interface{}, tabl data = MapOrStructToMapDeep(value, true) ) for fieldName, fieldValue := range data { + var fieldType = c.GetFieldTypeStr(ctx, fieldName, table, c.GetSchema()) data[fieldName], err = c.db.ConvertValueForField( ctx, - c.GetFieldTypeStr(ctx, fieldName, table, c.GetSchema()), + fieldType, fieldValue, ) if err != nil { @@ -124,41 +137,63 @@ Default: 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") + } else { + switch fieldType { + case fieldTypeYear: + convertedValue = r.Format("2006") + case fieldTypeDate: + convertedValue = r.Format("2006-01-02") + case fieldTypeTime: + convertedValue = r.Format("15:04:05") + default: + } } 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") + } else { + switch fieldType { + case fieldTypeYear: + convertedValue = r.Format("2006") + case fieldTypeDate: + convertedValue = r.Format("2006-01-02") + case fieldTypeTime: + convertedValue = r.Format("15:04:05") + default: + } } 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 + switch fieldType { + case fieldTypeYear: + convertedValue = r.Layout("2006") + case fieldTypeDate: + convertedValue = r.Layout("2006-01-02") + case fieldTypeTime: + convertedValue = r.Layout("15:04:05") + default: + 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 + switch fieldType { + case fieldTypeYear: + convertedValue = r.Layout("2006") + case fieldTypeDate: + convertedValue = r.Layout("2006-01-02") + case fieldTypeTime: + convertedValue = r.Layout("15:04:05") + default: + convertedValue = r.Time + } } case Counter, *Counter: @@ -181,6 +216,7 @@ Default: } } } + default: } return convertedValue, nil