-
Notifications
You must be signed in to change notification settings - Fork 116
go sqlgen: Add support for converting "Zero" value fields to NULL in the db #181
Conversation
4be26e4
to
350b7bb
Compare
Pull Request Test Coverage Report for Build 1135
💛 - Coveralls |
@@ -115,8 +119,7 @@ var _ driver.Valuer = &Valuer{} | |||
// into the type dictated by our descriptor. | |||
type Scanner struct { | |||
*Descriptor | |||
value reflect.Value | |||
isValid bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this not used anywhere? If so, hooray, goodbye allocation!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup!
internal/fields/sql.go
Outdated
@@ -77,6 +77,10 @@ func (f Valuer) Value() (driver.Value, error) { | |||
return iface.MarshalJSON() | |||
} | |||
return json.Marshal(i) | |||
case f.Tags.Contains("zeroIsNull"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nullable
? Trying to think of a better name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per offline convo: implicitnull
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changing to implicitNull
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer all lowercase since that's how go's stdlib does it (omitempty
).
sqlgen/zeronull_test.go
Outdated
@@ -0,0 +1,187 @@ | |||
package sqlgen | |||
|
|||
import ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine, though it'd be nice to have tests in the fields package given we test tons of other values there 🤷♂️. At this point it might be worth consolidating the fields package with sqlgen anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, adding some tests here and seeing edge cases, will update PR
Adding some tests for filters to make sure they still work properly. |
350b7bb
to
62f12b4
Compare
Updated! Also added a db test thing I've been using locally to get tests to run without an EnvVar every time. |
62f12b4
to
e392a12
Compare
internal/testfixtures/db.go
Outdated
@@ -28,6 +28,8 @@ var DefaultDBConfig = DBConfig{ | |||
func init() { | |||
if pw := os.Getenv("DB_PASSWORD"); pw != "" { | |||
DefaultDBConfig.Password = pw | |||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can change the password to just always be dev and update the docker container on here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once this section is removed and the DefaultDBConfig
's password reflects the docker container's password this PR is good to go! Make sure to update the CHANGELOG.md
with the changes, too!
Name: "Bob", | ||
Uuid: testfixtures.CustomType{'1', '1', '2', '3', '8', '4', '9', '1', '2', '9', '3'}, | ||
Mood: &mood, | ||
ImplicitNull: "test", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm - do we want this in our benchmark?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm excited with this change.
sqlgen/implicitnull_test.go
Outdated
}, rows) | ||
} | ||
|
||
func GetCount(db *sql.DB, countQuery string) (int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: don't export this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooc: can you do stuff with exported functions in a test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only from other test files I believe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
internal/fields/sql.go
Outdated
@@ -77,6 +77,10 @@ func (f Valuer) Value() (driver.Value, error) { | |||
return iface.MarshalJSON() | |||
} | |||
return json.Marshal(i) | |||
case f.Tags.Contains("implicitnull"): | |||
if IsZero(f.value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm excited by how small this change is that i missed it in my first read.
internal/fields/reflect.go
Outdated
// Mostly copied from https://github.com/golang/go/issues/7501 | ||
// Works for mostly everything except zero initialized arrays (e.g. | ||
// `var ray [5]string` will not be "Zero") | ||
func IsZero(v reflect.Value) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should we make this private?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stephen given that it's in an internal package, it's going to be private API one way or another - but I think that's a good point
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
sqlgen/implicitnull_test.go
Outdated
NullByte: []byte("hello there"), | ||
NullTime: timeField, | ||
} | ||
if _, err := db.InsertRow(context.Background(), newRow); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we add a test for UpdateRow
as well?
@@ -77,6 +77,10 @@ func (f Valuer) Value() (driver.Value, error) { | |||
return iface.MarshalJSON() | |||
} | |||
return json.Marshal(i) | |||
case f.Tags.Contains("implicitnull"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: what is implicit about this null? i kind of like zeroisnull
, but i could be convinced otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqlgen/implicitnull_test.go
Outdated
assert.Equal(t, []*ImplicitNull{ | ||
newRow, | ||
}, rows) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we also add a test for the reading that a null
in the db reads out as the zero value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from @berfarah:
let's verify that we have:
- integration test for livesql (binlog)
- integration test for sqlgen CRUD
- integration test for batching in sqlgen
e392a12
to
7cbb86a
Compare
Not to self: binlog and Delete still need tests. |
7cbb86a
to
69f376d
Compare
Updated! should be good to review now! |
69f376d
to
ada61ed
Compare
Summary: Changes the default test fixture to use "dev" as the password for databases.
Summary: Noticed isValid wasn't used outside of the Scan function, so removed it from the field and just initialized it in the Scan function.
Summary: Adds a function that can take a reflect.Value and return whether the provided value is the "Zero" value of that type. We'll use this to determine whether a provided type should be treated as "null" for the new "zeroIsNull" tag.
Summary: Adds a new tag `sql:",implicitnull"` we can use on thunder fields to mark that the "Zero" value of a field should be treated as NULL in the database.
ada61ed
to
80cb3cc
Compare
Summary: Adds support to sqlgen to convert "Zero" value model fields to NULL in
the database. This only needs to be applied when inserting a value to the
database because when we read from the db NULL values are noop (leaving the
zero value).
I put some cleanups in the PR as well, would be easy to pull them out if we want.
Additionally, I wrote tests for the "Main" use cases, but I'm very open
to add tests for other edge cases, let me know and I'll add them.