13

I am writing PostgreSQL table schema.

type TestTable struct {
    ID        int    `gorm:"column:id;primaryKey;autoIncrement"`
    CarType   string `gorm:"column:car_type"`
}

So how can i add car types like "SEDAN", "HATCHBACK", "MINIVAN" as enum data type

1

7 Answers 7

26

Assuming you are using GORM with PostgreSQL. First in your database create a type.

CREATE TYPE car_type AS ENUM (
    'SEDAN',
    'HATCHBACK',
    'MINIVAN');

Then you will need to define the following model:

import "database/sql/driver"

type carType string

const (
    SEDAN  carType = "SEDAN"
    HATCHBACK carType = "HATCHBACK"
    MINIVAN carType = "MINIVAN"
)

func (ct *carType) Scan(value interface{}) error {
    *ct = carType(value.([]byte))
    return nil
}

func (ct carType) Value() (driver.Value, error) {
    return string(ct), nil
}

type MyTable struct {
    gorm.Model
    CarType carType `gorm:"type:car_type"`
}

func (MyTable) TableName() string {
    return "my_table"
}

Note for MySQL users, you can add struct tag gorm: or sql: so you don't have to run raw query to create enum in the database.

CarType carType `gorm:"type:enum('SEDAN', 'HATCHBACK', 'MINIVAN')";"column:car_type"`

OR

CarType carType `sql:"type:ENUM('SEDAN', 'HATCHBACK', 'MINIVAN')" gorm:"column:car_type"`
Sign up to request clarification or add additional context in comments.

10 Comments

Thanks, Nick for the response, but is there no support for Enum datatype? how we do for other data types like numeric etc. In this, I need to write the raw query to create an enum type in DB first.
You are right. There is no datatype given in the language as enum but we can represent it. Follow the first 3 responses to the following question, it will help you understanding enums in golang . stackoverflow.com/questions/14426366/… On the other part, I think you can try without creating enum in the database. But still without it being enum in the database, you will be risking parsing non-constant values in the database to the constants in your program. I think it's unsafe.
I followed the same steps and also referred it from the main github issue on which this answer is based, But for me the line => CarType carType sql:"car_type" is just creating a text(string) column in postgres DB, I already have created the enum in my DB. I tried everything, no error is thrown, don't know whats the problem. Anyone having the same issue or resolution please reply.
@Mehta We made it work by using gorm:"type:car_type"
How would you make this (enum) work for a "Has Many" or "Many to Many" association?
|
3

Edit: someone pointed out this only works with MySQL. I would take my answer down but someone using MySQL may find it helpful.

Here's an answer that doesn't require you to create a SQL type beforehand. My source for this solution was this github issue

For your field tag, use this:

type TestTable struct {
    ID        int     `gorm:"column:id;primaryKey;autoIncrement"`
    CarType   carType `sql:"type:ENUM('SEDAN', 'HATCHBACK', 'MINIVAN')" gorm:"column:car_type"`
}

You'll also need to add the Scan and Value methods attached to your carType type.

type carType string

const (
    SEDAN carType = "SEDAN"
    HATCHBACK carType = "HATCHBACK"
    MINIVAN carType = "MINIVAN"
)

func (self *carType) Scan(value interface{}) error {
    *self = carType(value.([]byte))
    return nil
}

func (self carType) Value() (driver.Value, error) {
    return string(self), nil
}

Comments

2

in order to extend Nick's answer I am adding the following for automation:

Assuming you have DBClient struct, you can create a method that creates this car type:

func (psqlClient *DBClient) CreateCarTypeEnum() error {
    result := psqlClient.db.Exec("SELECT 1 FROM pg_type WHERE typname = 'car_type';")

    switch {
    case result.RowsAffected == 0:
        if err := psqlClient.db.Exec("CREATE TYPE car_type AS ENUM ('SEDAN', 'HATCHBACK', 'MINIVAN');").Error; err != nil {
            log.Error().Err(err).Msg("Error creating car_type ENUM")
            return err
        }

        return nil
    case result.Error != nil:
        return result.Error

    default:
        return nil
    }
}

Comments

1

on a side note- if you decide to go with slightly different approach: you can define your enums as int, and leverage iota. then you can use code generator to create sql Scaner/Valuer but also json/text representations. for example: https://github.com/dmarkham/enumer

Comments

1

Just like you write in sql, you can use type and add whatever datatype you want like you write in sql.

type MyTable struct {
    CarType    string `gorm:"column:car_type;type:enum('SEDAN','HATCHBACK','MINIVAN')" json:"car_type"`
}

2 Comments

This answer doesn't make use of gorm enumerations. I also tried this exact syntax and a sql field tag is required.
No, it doesn't require sql field tag. Which databae type do you use? I use in mysql. It works fine. no sql field tag is required.
0

An update, it will not work with sql:"car_type" instead use gorm:"car_type". That too once you have manually created the custom enum type into the database.

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
-2

https://gorm.io/docs/data_types.html => impl Scan & Value methods.


If enum is generated by protoc, can use this plugin

https://github.com/yangyang5214/protoc-gen-gorm-serializer .

# task.proto
syntax = "proto3";

enum TaskStatus {
  Unknown = 0;
  Running = 1;
  Exiting = 2;
  Pending = 3;
}


# gen gorm-serializer.pb.go
protoc --proto_path=.  --go_out=paths=source_relative:. --gorm-serializer_out=paths=source_relative:.  task.proto

# use 
type Task struct {
    gorm.Model
    Name   string
    Status example.TaskStatus `gorm:"type:int"`
}

1 Comment

If that doesn't work, by directly mapping fields to enums that are generated by proto. can use this plugin to gen Scan & Value method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.