-
Notifications
You must be signed in to change notification settings - Fork 122
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
Marshal missing UDT fields as null instead of failing #269
Marshal missing UDT fields as null instead of failing #269
Conversation
3ef9792
to
bdee62b
Compare
@avelanarius ping |
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 definitely should have test for that
udt.go
Outdated
@@ -39,11 +39,17 @@ func makeUDT(value reflect.Value, mapper *reflectx.Mapper, unsafe bool) udt { | |||
|
|||
func (u udt) MarshalUDT(name string, info gocql.TypeInfo) ([]byte, error) { | |||
value, ok := u.field[name] | |||
if !ok { | |||
return nil, fmt.Errorf("missing name %q in %s", name, u.value.Type()) | |||
|
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.
Should we consider unsafe
flag ?
func (u udt) MarshalUDT(name string, info gocql.TypeInfo) ([]byte, error) {
value, ok := u.field[name]
if ok {
return gocql.Marshal(info, value.Interface())
}
if u.unsafe {
return nil, nil
}
return nil, fmt.Errorf("missing name %q in %s", name, u.value.Type())
}
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.
@dkropachev AFAIK this would require a user to use query.Iter().Unsafe()
instead of just query.Exec()
, because in gocql
implementation there is no unsafe
equivalent. Wouldn't it be counterintuitive if in gocql
we are able to do query.Exec()
with udt with missing fields and it works, but in gocqlx
we are required to use Unsafe()
?
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.
@sylwiaszunejko , 100% agree on that, but since Unsafe is there and does control this particular behavior we have to be consistent in the way it works.
To sum up after a discussion with @dkropachev: |
I would propose to rename this attribute to |
bdee62b
to
31e64f5
Compare
I'm in favor of consistency, even if it's the longer path (and complexity). The unsafe is not intuitive. |
31e64f5
to
4d8c8ba
Compare
@mykaul , could you please rereview it, tests were added and more cases fixed. |
4d8c8ba
to
2239c1b
Compare
Do we have an example (in gocqlx docs) for UDT usage? |
iterx.go
Outdated
// - ptr to t implements gocql.Unmarshaler, gocql.UDTUnmarshaler or UDT | ||
// - it is not a struct | ||
// - it has no exported fields |
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.
Why this change? Asterisks are valid Markdown itemizers as well AFAIK.
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 one is gone, it is already in master, reason why it have changed is golangci-lint
does not tollerate anything by -
there.
func (u udt) UnmarshalUDT(name string, info gocql.TypeInfo, data []byte) error { | ||
value, ok := u.field[name] | ||
if !ok && !u.unsafe { | ||
return fmt.Errorf("missing name %q in %s", name, u.value.Type()) | ||
if ok { | ||
return gocql.Unmarshal(info, data, value.Addr().Interface()) | ||
} | ||
|
||
return gocql.Unmarshal(info, data, value.Addr().Interface()) | ||
if u.unsafe { | ||
return nil | ||
} | ||
return fmt.Errorf("missing name %q in %s", name, u.value.Type()) |
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.
Are we sure this is the semantics of unsafe
that we want to have?
In Rust driver at least, we consider too many and too few fields in the CQL UDT compared to the go struct as two distinct cases. In Rust driver, strict forbids too many fields in CQL UDT, whereas default_if_missing is used for the case when a struct has a field that is not found in the CQL UDT metadata. WDYT? Should we cover it by a single lever 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.
@wprzytula, i am totally in support of having all drivers as close as possible to each other. Let's have separate issue on that and address it in other PR
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.
OTOH I have no idea how other drivers apart from Rust driver approach 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.
We can start from these two
I'm strongly for the change from |
I agree, I would like to go with the @mykaul @avelanarius @roydahan Are we okay with making breaking change in that case? |
We can't return an error in case a field is added to the UDT, otherwise existing code would break by simply altering the UDT in the database. For extra fields at the end of the UDT put nulls to be in line with gocql, but also python-driver and java-driver. In gocql it was fixed in scylladb/gocql@d2ed1bb
It enables local control over `unsafe` mode for .Bind methods of `Queryx` and iterators spawn by it.
2239c1b
to
cdb2e11
Compare
…safe mode We can't return an error in case a field is added to the UDT, otherwise existing code would break by simply altering the UDT in the database. For extra fields at the end of the UDT put nulls to be in line with gocql, but also python-driver and java-driver. In gocql it was fixed in scylladb/gocql@d2ed1bb
cdb2e11
to
a12aa3e
Compare
Let's do that in separate PR. |
I'm fine with it, we can release a major and state that in the release notes. |
Let's merge it and then we can replace unsafe with strict approach in the additional PR. Are you okay with that? |
Interesting. |
I'd refrain from bumping the major version and only do that in case of major API breakage i.e. new API, preferably never. Bumping it to v2 was a complete mess, every import line needs to be changed. |
@mmatczuk do you have any suggestion how to handle better the situation with Unsafe and how the default behavior around dealing with missing fields in UDTs differs in gocql and gocqlx? |
The change is LGTM. |
I am not sure if I understand right, is it okay to do breaking change without major release? |
What I'm trying to say is that there is a cost of making /v3 it is not something to do lightly. |
Just want to point out that gocqlx recently switched to scylladb/gocql, which also contributes to necessity to bump major up. |
We can't return an error in case a field is added to the UDT, otherwise existing code would break by simply altering the UDT in the database. For extra fields at the end of the UDT put nulls to be in line with gocql, but also python-driver and java-driver.
In gocql it was fixed in scylladb/gocql@d2ed1bb
java-driver https://github.com/datastax/java-driver/blob/ef56d561d97adcae48e0e6e8807f334aedc0d783/core/src/main/java/com/datastax/oss/driver/internal/core/type/codec/UdtCodec.java#L86
python-driver: https://github.com/datastax/python-driver/blob/15d715f4e686032b02ce785eca1d176d2b25e32b/cassandra/cqltypes.py#L1036