-
Notifications
You must be signed in to change notification settings - Fork 855
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementations of driver.Valuer are no longer called for nil receivers in v5 #1566
Comments
It seems like your concerns were partially answered and resolved in subsequent minor and patch releases, right? At least ours were with the latest 5.3.1. |
Sorry, I actually don't know any good way for an application to get that behavior back. Typed nils caused a lot of headaches in pgtype and normalizing them at the border simplified things quite a bit. It wasn't clear to me at the time that this would have negative downstream effects. While calling methods with a nil receiver is allowed in Go, it almost always is an error. But I suppose it is a reasonable thing to consider with your example type. As far as changes to pgx that could improve this use case, there are only two options that come to mind.
Not sure either of these is a good idea though. I'm not confident that any typed nils allowed past the border wouldn't crash later in code not expecting a nil. Maybe if it was only enabled for database/sql though... |
The patch used in gobuffalo/pop does use
The most common use case for this behavior is custom types that transform e.g. JSON
that would be hydrated by a JSON unmarshaller. Here, the JSON unmarshaller will set
A global flag would probably be the "easiest" way from a consumer perspective to keep backwards compatibility, in particular for cases where pgx is used in an DBAL library such as pop or others. A type assertion would certainly be more elegant, but will potentailly cause downstream issues when users simply update the underlying DBAL. |
So just out of curiosity I disabled the typed nil to nil conversion to see what would happen. I did this by changing all the functions in the It broke It also caused test failures with JSON encoding of typed nils. Typed nils would be encoded as the JSON It also broke There's actually less failures than I expected, though that could be due lack of extensive tests for typed nils. If this is to be configured, then based on where |
That's nice to hear! I'm happy to test any changes in PGX against our existing code bases to see if anything breaks - just let me know the correct git hash for go mod replace :) |
@jackc do you have an idea on how would be the best way to proceed? We can help if you slightly guide us on where we should be focusing on. Thanks! 🤞 |
@glerchundi If you want, you can prototype the approach in my previous comment. I think most of it is pretty straight forward. The part I'm not sure about is how to configure it. I'm nervous about nils leaking in and causing panics, so I kind of lean toward something that can change the behavior per type rather than just a flag, but that might be overcomplicating it.. |
Ok, thanks for the info @jackc. Lets see what we can do on our side and will let you know. Although it could overcomplicate it, I’m also in favor of per type approach. |
Curious if anyone found a nice solution? @glerchundi |
We did not found a free slot to jump into this yet. Hopefully in September. |
This also breaks gorm for a long time now, the current "workaround" is to add exclude-primitives every time a new version is tagged: go-gorm/postgres#159 |
Had to update exclude-directives again to fix broken builds. The current list, when using gorm and postgres is as follows:
|
@jackc @glerchundi - I added a PR for this. When you get a chance, could you take a look and let me know if it is on the right track. Thank you! |
pgx v5 introduced nil normalization for typed nils. This means that []byte(nil) is normalized to nil at the edge of the encoding system. This simplified encoding logic as nil could be encoded as NULL and type specific handling was unneeded. However, database/sql compatibility requires Value to be called on a nil pointer that implements driver.Valuer. This was broken by normalizing to nil. This commit changes the normalization logic to not normalize pointers that directly implement driver.Valuer to nil. It still normalizes pointers that implement driver.Valuer through implicit derefence. e.g. type T struct{} func (t *T) Value() (driver.Value, error) { return nil, nil } type S struct{} func (s S) Value() (driver.Value, error) { return nil, nil } (*T)(nil) will not be normalized to nil but (*S)(nil) will be. #1566
I think I have a solution in #2019. Basically, it just skips nil normalization on any |
Merged #2019. |
The v5 changelog mentions this:
That's a huge change in behavior from v4. Previously, it was very simple to implement custom types which would serialize themselves into a non-SQL-NULL value from a nil receiver.
This would work in v4, but fails in v5 with a NOT NULL constraint violation, because
(MySlice).Value()
is never called.Is there a comparably simple way to achieve this pattern in pgx/v5?
Thanks for your work and this great library!
The text was updated successfully, but these errors were encountered: