20

I am creating a gorm model

// Day is a corresponding day entry
type Day struct {
    gorm.Model
    Dateday   string         `json:"dateday" gorm:"type:date;NOT NULL"`
    Nameday   string         `json:"nameday" gorm:"type:varchar(100);NOT NULL"`
    Something sql.NullString `json:"salad"`
    Holyday   bool           `json:"holyday"`
}

I am using sql.NullString for the field Something cause it may be NULL.

So when I try to execute a typical gorm example to validate my setup works:

    db.Create(&Day{
        Nameday:     "Monday",
        Dateday:     "23-10-2019",
        Something:   "a string goes here",
        Holyday:      false,
    })

I get:

cannot use "a string goes here", (type string) as type sql.NullString in field value

What type should I use for the Something field given it may be NULL?

3
  • 2
    sql.NullString is a struct type and is not convertible to a string type. You need to initialize a value of sql.NullString and set that to the field. One way to do that would be Something: sql.NullString{String: "a string goes here", Valid: true}. golang.org/pkg/database/sql/#NullString Commented Mar 21, 2020 at 19:31
  • thanks for taking the time to respond; I would suggest you post it as a normal answer so I can accept / upvote it Commented Mar 21, 2020 at 20:18
  • 1
    If I remember correctly you can use *string type for NULL-able columns Commented Mar 22, 2020 at 5:40

2 Answers 2

34

The sql.NullString type is not actually a string type but a struct type. It's defined as:

type NullString struct {
    String string
    Valid  bool // Valid is true if String is not NULL
}

Therefore you need to initialize it as such:

db.Create(&Day{
    Nameday:     "Monday",
    Dateday:     "23-10-2019",
    Something:   sql.NullString{String: "a string goes here", Valid: true},
    Holyday:     false,
})

As an alternative, if you want to keep using the simpler syntax when initializing a nullable string, you could declare your own nullable string type, have it implement the sql.Scanner and driver.Valuer interfaces, and leverage the null byte to signal a NULL value.

type MyString string

const MyStringNull MyString = "\x00"

// implements driver.Valuer, will be invoked automatically when written to the db
func (s MyString) Value() (driver.Value, error) {
    if s == MyStringNull {
        return nil, nil
    }
    return []byte(s), nil
}

// implements sql.Scanner, will be invoked automatically when read from the db
func (s *MyString) Scan(src interface{}) error {
    switch v := src.(type) {
    case string:
        *s = MyString(v)
    case []byte:
        *s = MyString(v)
    case nil:
        *s = MyStringNull
    }
    return nil
}

With this, if you declare the field Something to be of type MyString you can initialize it as you originally intended.

db.Create(&Day{
    Nameday:     "Monday",
    Dateday:     "23-10-2019",
    // here the string expression is an *untyped* string constant
    // that will be implicitly converted to MyString because
    // both `string` and `MyString` have the same *underlying* type.
    Something:   "a string goes here",
    Holyday:     false,
})

Just keep in mind that this works only with untyped constants, once you have a constant or variable of type string, to be able to assign that to a MyString you'll need to use an explicit conversion.

var s string
var ms MyString

s = "a string goes here"
ms = s // won't compile because s is not an untyped constant
ms = MyString(s) // you have to explicitly convert
Sign up to request clarification or add additional context in comments.

2 Comments

Something: sql.NullString{String: "a string goes here", Valid: true}, worked out for me, thank you.
Really nice answer. A small typo in Scan function. It should return MyStringNull in case of nil, and not StringNull. Too small of an edit so SO doesn't allow me to suggest it.
2
package main

import (
    "github.com/guregu/null"
)

func main() {

    db.Create(&Day{
        Nameday: null.StringFrom("Monday"),
    })
}

Comments

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.