pgtype
pgtype copied to clipboard
`JSONB.Set(src)` does not support nil sources of type other than `*string` and `[]byte`
When I call JSONB.Set(src) where src is a nil map or slice, I expect it to be treated as null. Instead, it is treated as "null". map[string]interface{} and []map[string]interface{} are common types used for JSONB columns. Supporting nil values for them would be nice.
func TestJSONB(t *testing.T) {
t.Run("map", func(t *testing.T) {
var jsonb pgtype.JSONB
jsonb.Set((map[string]interface{})(nil))
if jsonb.Status != pgtype.Null {
t.Error("JSONB should be null")
}
})
t.Run("slice", func(t *testing.T) {
var jsonb pgtype.JSONB
jsonb.Set(([]map[string]interface{})(nil))
if jsonb.Status != pgtype.Null {
t.Error("JSONB should be null")
}
})
}
When I call JSONB.Set(src) where src is a nil map or slice, I expect it to be treated as null.
It is. The problem is "which null"? That is, the SQL value NULL and the JSON value null are two different things.
Instead, it is treated as "null"
To be precise it is JSON null not JSON "null".
The current implementation treats nil as SQL NULL. *string and []byte typed nil is also treated as SQL NULL because those types are not encoded into JSON -- they are treated as JSON.
I don't think it would make sense to special case certain types of nil. What about every type of slice? It would need to be handled for all types with reflection. And even then there are some fun edge cases. What about (*T)(nil) when (*T) implements MarshalJSON()?
I think this is just subtle distinction that application code will need to be aware of. (Or you could register your own type that handles JSON differently -- the JSON type is pretty trivial.)
Sorry for not being more explicit about jSON null and SQL null (I was referring to the value sent through the text-based connection).
The current implementation treats nil as SQL NULL
Actually, it treats nil as JSON null (See my tests).
I don't think it would make sense to special case certain types of nil
I agree. That's why treating (*string)(nil) and (map[string]any)(nil) differently did not make sense to me. I would expect all nil values to be treated as SQL NULL. "null" or []byte{'n', 'u', 'l', 'l'} can be treated as JSON NULL. Also, we can have a helper method to set JSONB object to JSON NULL. Something like pgtype.JSONB.SetNull().
The current implementation treats nil as SQL NULL
Actually, it treats nil as JSON null (See my tests).
By nil I meant the untyped nil as opposed to typed nil (https://go.dev/doc/faq#nil_error).
I would expect all nil values to be treated as SQL NULL.
I think v5 will do this. It isn't specific for json, but query methods now normalize typed nil arguments to untyped nil. So I don't mind the change in principle, I guess the question is if someone is somehow relying on the existing behavior.
query methods now normalize typed nil arguments to untyped nil
I think this is the most convenient way to handle it. Because we pass concrete values as parameters to these methods most of the time. When the interface value is nil, it should be treated as nil.
Also, solving this in v5 also makes sense. No need to cause issues for the ones who rely on this bevahour in v4 in my opinion.