3

I have a problem where Go is inserting "null" into my PostgreSQL database's jsonb columns if I try to do the following, and the structs property (in this case of type map[string]interface{}) is empty:

accessMembers, _ := json.Marshal(c.AccessMembers)

Doing a test print it outputs the same as the value which get stored into the database:

fmt.Println(string(accessMembers)) // equals the string "null"

The problem is that I need it to be - nil (not a string, but the Golang nil), so when I use it in the Exec function below:

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, accessMembers, c.Id)

It should be the equivalent (which works!!) of doing:

_, err := db.Exec(sqlStr, nil, c.Id)

I tried a lot of different workarounds, which I thought had to work, but they didn't.

eg.

var accessMembers []byte

am, _ := json.Marshal(c.AccessMembers)

if string(am) != "null"{
    accessMembers = am
}

sqlStr := `UPDATE my_table SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, accessMembers, c.Id)

Which results in the following error: "pq: invalid input syntax for type json"

I can't understand why this is not the same, as explicitly writing nil in the Exec functions parameter, since the []byte clearly must be nil, right?

What am I doing wrong here? Here's how I had hoped it would work: http://play.golang.org/p/UoLAGfhhRl

Answer:

var accessMembers interface{} = nil

if c.AccessMembers != nil {
    j, _ := json.Marshal(c.AccessMembers)
    accessMembers = j
}

Thanks to thwd for providing the solution basically, and for pointing out how the database driver marshals nil differently, suggesting interface{} instead of []byte.

Thanks to DonSeba for reminding me to check if the struct field exists before using resources to marshal, do string conversion and comparison, which ought to be more expensive.

2 Answers 2

3

I can't understand why this is not the same, as explicitly writing nil in the Exec functions parameter, since the []byte clearly must be nil, right?

nil has a type. The database driver marshals nil differently for type map[string]interface{} or []byte than for interface{} (which is the type of the second argument of db.Exec). You should be able to do:

var i interface{} = nil

j, _ := json.Marshal(c.AccessMembers)

if string(j) != "null" {
     i = j
}

sqlStr := `UPDATE content SET access_members=$1 WHERE id=$2;`

_, err := db.Exec(sqlStr, i, c.Id)
Sign up to request clarification or add additional context in comments.

Comments

1

You have not initialized your AccessMembers.

Before you decide to marshal the value you should check if it exists.

if c.AccessMembers == nil {
    c.AccessMembers = make(map[string]interface{})
}

If you do the above, the result of :

accessMembers, err := json.Marshal(c.AccessMembers)

will be an valid empty json value {}, and valid for storing into the db and retrieving the value later.

UPDATE :

there has been some discussion about the "NULL" value here : https://code.google.com/p/go/issues/detail?id=2278

2 Comments

Thanks for your suggestion. It does solve the error, but still the inserted value will be that empty map "{}" in my jsonb column, where I instead need the map to marshal to NULL like when I input - nil - directly in the Exec function as parameter. I have read the link, but as I see it, there's no real solution in that thread :/
Since json.Marshal() cannot "see|access" c.AccessMembers it returns "NULL" just the way it works...

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.