diff --git a/contrib/drivers/mysql/mysql_basic_test.go b/contrib/drivers/mysql/mysql_basic_test.go index 6e028ae8b62..0f4c5403290 100644 --- a/contrib/drivers/mysql/mysql_basic_test.go +++ b/contrib/drivers/mysql/mysql_basic_test.go @@ -11,7 +11,9 @@ import ( "testing" "github.com/go-sql-driver/mysql" + "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/test/gtest" ) @@ -42,6 +44,47 @@ func Test_Func_ConvertDataForRecord(t *testing.T) { t.Assert(len(m), 1) t.Assert(m["reset_password_token_at"], nil) }) + + type TestNil struct { + JsonEmptyString *gjson.Json `orm:"json_empty_string"` + JsonNil *gjson.Json `orm:"json_nil"` + JsonNull *gjson.Json `orm:"json_null"` + VarEmptyString *gvar.Var `orm:"var_empty_string"` + VarNil *gvar.Var `orm:"var_nil"` + } + gtest.C(t, func(t *gtest.T) { + c := &gdb.Core{} + m, err := c.ConvertDataForRecord(nil, TestNil{ + JsonEmptyString: gjson.New(""), + JsonNil: gjson.New(nil), + JsonNull: gjson.New(struct{}{}), + VarEmptyString: gvar.New(""), + VarNil: gvar.New(nil), + }) + + t.AssertNil(err) + t.Assert(len(m), 5) + + valueEmptyString, exist := m["json_empty_string"] + t.Assert(exist, true) + t.Assert(valueEmptyString, nil) + + valueNil, exist := m["json_nil"] + t.Assert(exist, true) + t.Assert(valueNil, nil) + + valueNull, exist := m["json_null"] + t.Assert(exist, true) + t.Assert(valueNull, "null") + + valueEmptyString, exist = m["var_empty_string"] + t.Assert(exist, true) + t.Assert(valueEmptyString, "") + + valueNil, exist = m["var_nil"] + t.Assert(exist, true) + t.Assert(valueNil, nil) + }) } func Test_Func_FormatSqlWithArgs(t *testing.T) { diff --git a/contrib/drivers/mysql/mysql_core_test.go b/contrib/drivers/mysql/mysql_core_test.go index dbb61923451..00cc599bc61 100644 --- a/contrib/drivers/mysql/mysql_core_test.go +++ b/contrib/drivers/mysql/mysql_core_test.go @@ -258,6 +258,51 @@ func Test_DB_Insert_KeyFieldNameMapping(t *testing.T) { }) } +func Test_DB_Insert_NilGjson(t *testing.T) { + var tableName = "nil" + gtime.TimestampNanoStr() + _, err := db.Exec(ctx, fmt.Sprintf(` + CREATE TABLE IF NOT EXISTS %s ( + id int(10) unsigned NOT NULL AUTO_INCREMENT, + json_empty_string json DEFAULT NULL, + json_nil json DEFAULT NULL, + json_null json DEFAULT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `, tableName)) + if err != nil { + gtest.Fatal(err) + } + defer dropTable(tableName) + + gtest.C(t, func(t *gtest.T) { + type Json struct { + Id int + JsonEmptyString *gjson.Json + JsonNil *gjson.Json + JsonNull *gjson.Json + } + + data := Json{ + Id: 1, + JsonEmptyString: gjson.New(""), + JsonNil: gjson.New(nil), + JsonNull: gjson.New(struct{}{}), + } + + _, err = db.Insert(ctx, tableName, data) + t.AssertNil(err) + + one, err := db.GetOne(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", tableName), 1) + t.AssertNil(err) + + t.AssertEQ(len(one), 4) + + t.Assert(one["json_empty_string"], nil) + t.Assert(one["json_nil"], nil) + t.Assert(one["json_null"], "null") + }) +} + func Test_DB_Upadte_KeyFieldNameMapping(t *testing.T) { table := createInitTable() defer dropTable(table) diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index cedcb19b109..a6117630558 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -106,8 +106,13 @@ func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{}) // Nothing to do. default: - // Use string conversion in default. - if s, ok := value.(iString); ok { + // If `value` implements interface iNil, + // check its IsNil() function, if got ture, + // which will insert/update the value to database as "null". + if v, ok := value.(iNil); ok && v.IsNil() { + convertedValue = nil + } else if s, ok := value.(iString); ok { + // Use string conversion in default. convertedValue = s.String() } else { // Convert the value to JSON. diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index cde375edf51..a59bf878c88 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -44,6 +44,11 @@ type iInterfaces interface { Interfaces() []interface{} } +// iNil if the type assert api for IsNil. +type iNil interface { + IsNil() bool +} + // iTableName is the interface for retrieving table name for struct. type iTableName interface { TableName() string