I'm new to Go and I'm trying to write a little program to save enumerated values to a database. The way I declare my values is as follows:
type FileType int64
const (
movie FileType = iota
music
book
etc
)
I use these values in my struct like this:
type File struct {
Name string
Type FileType
Size int64
}
I use gorp for my database stuff, but I guess the use of gorp isn't relevant to my problem. I put stuff in my DB like this:
dbmap.Insert(&File{"MyBook.pdf",movie,1000})
but when I try to retrieve stuff…
dbmap.Select(&dbFiles, "select * from Files")
I get the following error:
panic: reflect.Set: value of type int64 is not assignable to type main.FileType
When I use int64
as the type for the const(...)
and for the File.Type
field, everything works fine, but I'm new to Go and want to understand the problem.
The way I see it, I have two problems:
I figured out, that one can implement the sql.Scanner
interface by implementing the following method:
Scan(src interface{}) error
I tried to implement the method and I even was able to get the right value from src
and convert it to a FileType
, but I was confused if I should implement the method for "(f *FileType)
or (f FileType)
. Either way the method gets invoked, however I'm not able to overwrite f
(or at least the update gets lost later) and the File
instances read from the DB always had a "0" as value for File.Type
.
Do you have any ideas on those two points?
By keeping the enum in your database, and adding a foreign key on the table that contains an enum value you ensure that no code ever enters incorrect values for that column. This helps your data integrity and is the most obvious reason IMO you should have tables for enums.
A standard enum is usually implemented as an int32, the compiler will handle your enum as a synonym of int32 . Once a list of values is created for a enumeration those values are stored as literals against their display name(access name given at the time of declaration of enum).
Implementation Details. Enumerated (enum) types are data types that comprise a static, ordered set of values. They are equivalent to the enum types supported in a number of programming languages. An example of an enum type might be the days of the week, or a set of status values for a piece of data.
The ENUM data type is stored in two locations: the set of values is stored in the table metadata; in each row, only the set index is stored, as integer, which requires one byte for enums up to 255 entries large, then two for up to 65535 entries (see MySQL reference)
I recently had the same need, and the solution is to implement two interfaces:
Here's a working example:
type FileType int64
func (u *FileType) Scan(value interface{}) error { *u = FileType(value.(int64)); return nil }
func (u FileType) Value() (driver.Value, error) { return int64(u), nil }
Slightly off-topic, but may be useful to others as I kept revisiting this question/answer when solving a similar problem when working with postgres enum fields in golang (which are returned as bytes).
// Status values
const (
incomplete Status = "incomplete"
complete Status = "complete"
reject Status = "reject"
)
type Status string
func (s *Status) Scan(value interface{}) error {
asBytes, ok := value.([]byte)
if !ok {
return errors.New("Scan source is not []byte")
}
*s = Status(string(asBytes))
return nil
}
func (s SubjectStatus) Value() (driver.Value, error) {
// validation would go here
return string(s), nil
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With