I want to define custom sql type enum
like this:
package db
import (
"database/sql/driver"
"github.com/spf13/cast"
)
type Enum int64
func (e Enum) Value() (driver.Value, error) {
return int64(e), nil
}
func (e *Enum) Scan(v any) error {
*e = Enum(cast.ToInt64(v))
return nil
}
And I want use the following:
package db_test
import (
"db"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"testing"
)
type StatusEnum = db.Enum
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
// this is error because this type alias can not define new method
//func (e *StatusEnum) String() string{
// if v,ok := StatusEnumMap[*e];ok{
// return v
// }
// return "unknown"
//}
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
// SELECT * FROM `modules` WHERE status=1 ORDER BY `modules`.`status` LIMIT 1
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Log(StatusEnumMap[out.Status])
//t.Log(out.Status.String())
}
Now I'm using it like this StatusEnumMap[EnumValue]
when I want the string format. I think value.String()
is more elegant and simple. fmt
formatting can also print strings instead of numbers.
How can I better implement this feature? Thank you for your suggestions.
The above question has been answered by @Zeke Lu.
Another little problem, If I did not used Valuer
and delete the String
method, the sql condition status=1
will be status="1"
, why? Whether the query performance of sql will be affected if the type inconsistency in mysql.
The second problem:just is gorm debug info. Actual execute sql is status=1
. See gorm-issue
It seems that you don't need to implement the Valuer
and Scanner
interfaces if the underlying type is int64
. If that's true, the type Enum
is not needed. The following example works without Enum
:
Note: I modified your demo slightly to implement the Stringer
interface on type StatusEnum
instead of *StatusEnum
.
package db_test
import (
"testing"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type StatusEnum int64
const (
StatusEnumEnable StatusEnum = iota
StatusEnumDisable
)
var StatusEnumMap = map[StatusEnum]string{
StatusEnumEnable: "Enable",
StatusEnumDisable: "Disable",
}
func (e StatusEnum) String() string {
if v, ok := StatusEnumMap[e]; ok {
return v
}
return "unknown"
}
type module struct {
Status StatusEnum
}
func TestEnum(t *testing.T) {
db, _ := gorm.Open(sqlite.Open(":memory:"))
db.AutoMigrate(&module{})
db.Model(&module{}).Create(&module{Status: StatusEnumDisable})
var out *module
// SELECT * FROM `modules` WHERE status=1 ORDER BY `modules`.`status` LIMIT 1
db.Debug().Model(&module{}).Where("status=?", StatusEnumDisable).First(&out)
t.Log(out)
t.Logf("status value: %d, status string: %s", out.Status, out.Status)
}
The output is:
&{Disable}
status value: 1, status string: Disable
Please note that the first one is &{Disable}
now. It's &{1}
before.