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
feat(spanner): add support for JSON data type #4104
Conversation
I ran into the same problem in NodeJS, and there I made the following choice:
|
Could you elaborate a little on what you mean with this? If I remember correctly, the Go client will try to encode a Go |
So you mean you check whether or not every top-level element in an array can be encode to JSON. If yes, then it will be encoded as
The definition of func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
} If you specify the value to a string, then it will be encoded to a string, right? How can you tell |
Yes, correct. google-cloud-go/spanner/value.go Lines 2937 to 2959 in 1547ebd
As you can see, when value is a Go struct value, it can be encoded as a JSON. The code to convert Go struct can never be reached any more. It tries to encode a struct to json first. But I can't put This issue leads to TestBindParamsDynamic fails. The issue becomes how |
No, not exactly that. What I did in NodeJS was to assume that if the type is array, then the value should be encoded as some type of Cloud Spanner array. The array element type is then determined by the same encode function when the individual array elements are passed in to it. If the user has created an array with different types in it, then this will fail, but it is not checked beforehand.
Hmmm... Yeah, that will probably not work for Go in that case (at least not without some extensive refactoring). In Node, the user specifies the Cloud Spanner type themselves, so they pass in both a value and the Cloud Spanner type. So that made it possible for the user to pass in a string value and still set the type to be JSON. The encoded value (that is; the proto value) is encoded as string for both 'normal' strings and JSON, so that does not make any difference. |
@zoercai @skuruppu I think you need to chime in here as well. This is going to be a bit of a problem in the Go client, unless we accept doing a breaking change here. The short description of the problem is:
type User struct {
UserId string
UserName string
}
What do we want to do about this? I can think of a couple of possibilities:
@hengfengli Please feel free to add any other option you might see that I've missed, or to correct me if I misunderstood anything here. I'm not sure how easy/hard it would be to implement either of options 2 or 3. I think that either of these options could also be used to solve the problem of encoding top-level JSON arrays. As also noted in the NodeJS PR: It is not possible to determine based purely on the value whether a top-level JSON array should be encoded as |
I like the sound of option 3, assuming allowing users to specify the type would allow the |
@olavloite thanks for raising this. Unfortunately, when it comes to making breaking changes, our hands are tied. We essentially can't make changes that require users to change their code. So we have to go with option 3. |
@olavloite Thanks for a lot for summarizing the problem and providing possible options. I would vote for option 3 as well. The only question is that if we add this additional function, it means that the encoding will not happen automatically and the user has to call this function to encode the value or find a way to pass What if we don't add this additional function, instead, we only support the Also, with |
That is certainly also a possibility. I think the only drawback is that it would make |
We may get more types in the near future. So 1-1 mapping between Cloud Spanner and Go types cannot be held for a long time. I will go to the |
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 looks mostly good to me, with a couple of nits on adding some tests for some corner cases.
I'm fine with the choice of only supporting NullJSON
when encoding JSON types. While reviewing this PR, I did however notice one small thing that could cause a little confusion: The current implementation does support decoding JSON values to Go Struct types, but it does not support encoding Go Struct types to JSON. That asymmetry could cause confusion, so we should be sure to note it in the documentation.
One possible other solution for the problem regarding whether to encode a Go Struct to a Spanner STRUCT
or JSON
could be to use the same strategy as for the loss of precision check for NUMERIC
; configure it through a global setting. I'm not really sure I would be a proponent for such a solution, as adding too many global settings will not make the client easy to use. But in this case I'm also not sure whether the feature of encoding Go Struct to a Spanner STRUCT
is something that is used very much, as Spanner STRUCT
types cannot be used as the type of a column.
That's my worry as well. To avoid confusion, I'll take a look to see if decoding JSON values to Go Struct can be disallowed.
I agree with you that I would prefer not to add more global settings. I believe users still use |
@hengfengli Is this ready for another review? |
Yes, please take another review. Thanks a lot. |
Looks like this PR still needs an owners approval, could an owner please review this? @skuruppu @googleapis/yoshi-go-admins |
4e021ab
to
6b33476
Compare
Add support for JSON data type.
What:
Issues:
Array<JSON>
: Unable to encode[]Message{msg, msg}
toArray<JSON>
because we can't decide whether it should be mapped to aJSON
or aArray<JSON>
. (Resolved by only allowing to encode NullJSON so the Go types,NullJSON
andArray<NullJSON>
, will be encoded as Cloud Spanner types,JSON
andArray<JSON>
.)Message
, in itsValue
field. However, when we decode to a NullJSON, the type ofValue
field would bemap[string]interface{}
or[]interface{}
. This inconsistency is confusing.encodeStruct
: it seems that the json support conflicts with the currentencodeStruct
method. (Resolved by only allowing to encode NullJSON. No changes to the default behaviour.)