7

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

deepika azad
  • 131
  • 1
  • 1
  • 6
  • 1
    Reffer to this github issue answer as manual - https://github.com/go-gorm/gorm/issues/1978#issuecomment-476673540 – Oleg Butuzov Aug 03 '21 at 14:02

7 Answers7

22

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"`
Nick
  • 1,017
  • 1
  • 10
  • 16
  • 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. – deepika azad Aug 04 '21 at 09:47
  • 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 . https://stackoverflow.com/questions/14426366/what-is-an-idiomatic-way-of-representing-enums-in-go 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. – Nick Aug 04 '21 at 10:24
  • 2
    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 Dec 28 '21 at 06:46
  • 2
    @Mehta We made it work by using `gorm:"type:car_type"` – Roman Droppa Mar 30 '22 at 08:54
  • How would you make this (enum) work for a "Has Many" or "Many to Many" association? – BARJ Apr 04 '22 at 13:30
  • @RomanDroppa mind sharing your solution as answer? looking for similar..thanks – uberrebu Jul 30 '22 at 12:22
  • Ideally you should not need to run a custom sql query when using an ORM. This answer technically works, but there is a way to do this without the need to run a custom sql migration/query. My answer gives an example of how to do this without an additional query. – Cody Potter Oct 13 '22 at 20:39
  • @CodyPotter The question is about PostgreSQL and your solution will not work with PostgreSQL. Your code will create a text column in postgres. As this question's title doesn't mention postgres, will put up a note for mysql. – Nick Oct 14 '22 at 14:47
  • Ah good catch. I didn’t realize the question was specific to Postgres. I have to imagine there’s a way to do this in Postgres without an additional query though. If not I’d consider it a gorm bug. – Cody Potter Oct 15 '22 at 15:05
2

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
}
Cody Potter
  • 160
  • 1
  • 11
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

mikewyz
  • 21
  • 1
  • 3
1

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
    }
}
Sabuhi Shukurov
  • 1,616
  • 16
  • 17
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"`
}
Sithu Soe
  • 29
  • 4
  • This answer doesn't make use of gorm enumerations. I also tried this exact syntax and a sql field tag is required. – Cody Potter Oct 13 '22 at 20:51
  • 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. – Sithu Soe Oct 15 '22 at 03:00
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.

  • 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](/help/how-to-answer). – Community Jun 30 '22 at 16:16
-1

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"`
}
beer
  • 42
  • 5
  • 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. – beer Aug 06 '23 at 03:04