-
Notifications
You must be signed in to change notification settings - Fork 1.5k
fix(spanner): decoding spanner rows using SelectAll should map values in correct annotations #13301
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
Conversation
… in correct annotations
Summary of ChangesHello @rahul2393, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a critical issue in the Spanner client library's Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
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.
Code Review
This pull request addresses a bug in SelectAll where Spanner columns were not being correctly mapped to struct fields when spanner tags were used and column order did not match struct field order. The changes introduce a name-based mapping mechanism that correctly handles out-of-order columns and embedded structs, which is a solid improvement. The new test cases are comprehensive and validate the fix effectively. My review includes one suggestion to remove a redundant block of code which, while not affecting correctness, impacts performance and maintainability.
| if !isPrimitive { | ||
| e := sliceItem.Elem() | ||
| idx := 0 | ||
| for _, p := range pointers { | ||
| for i, p := range pointers { | ||
| if p == nil { | ||
| continue | ||
| } | ||
| e.Field(idx).Set(reflect.ValueOf(p).Elem()) | ||
| idx++ | ||
| indexPath := pointerIndexes[i] | ||
| if len(indexPath) == 0 { | ||
| continue | ||
| } | ||
| e.FieldByIndex(indexPath).Set(reflect.ValueOf(p).Elem()) | ||
| } | ||
| } |
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 if !isPrimitive block appears to be redundant and can be removed. The row.Columns(rowPointers...) call on line 518 already decodes the row values directly into the correct struct fields via the orderedPointers slice.
The orderedPointers slice is correctly constructed for each row to map column names to the corresponding struct field pointers. Therefore, after row.Columns executes, the sliceItem struct is already correctly populated.
This loop then iterates through the pointers, reads the values that were just decoded into the struct fields, and writes them back to the exact same fields. This is effectively a no-op (field.Set(field.Value())) and adds unnecessary overhead for each row processed.
Removing this block will improve performance and simplify the code.
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.
That block is still doing real work. The pointer reuse optimisation keeps a single slice of pointers that always references the struct value built during the first row. On subsequent rows we don’t rebuild those pointers—we simply reshuffle them (orderedPointers) and hand them to row.Columns. Because of that, every call to row.Columns writes into the first row’s struct, not the current sliceItem. The loop you highlighted copies the freshly decoded values from that shared pointer slice into the per-row sliceItem so we can append the correct struct to the destination.
If we drop the loop, only the first row would decode correctly. Later rows would append zero values (or stale data) because sliceItem never receives the decoded data. You can see this by temporarily removing the loop and rerunning TestSelectAll: the new out-of-order tag cases fail immediately.
So the block isn’t redundant—it’s required to make pointer reuse safe, while still benefiting from the optimisation.
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.
Warning
You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!
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.
Thank you, @rahul2393, for the detailed explanation. Your clarification regarding the pointer reuse optimization and how row.Columns writes into the first row's struct, necessitating this loop to copy values into the current sliceItem, is very clear. I understand now that this block is indeed crucial for correct decoding across all rows and is not redundant. My apologies for the initial misassessment.
I appreciate you taking the time to explain the underlying mechanism and the impact of removing the loop. This insight is valuable.
|
/gemini review |
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.
Code Review
This pull request correctly fixes a bug in SelectAll where decoding Spanner rows into structs failed if the column order did not match the struct field order. The solution cleverly remaps pointers for each row based on column names, ensuring correct data placement. A significant improvement is the added support for decoding into embedded structs, which is enabled by tracking field index paths and using FieldByIndex. The new test cases are thorough and validate both the main fix and the new embedded struct functionality. I have a couple of suggestions to further refine the code by removing a redundant block and optimizing a reflection-based lookup.
PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v1.0.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/librarian-go@sha256:718167d5c23ed389b41f617b3a00ac839bdd938a6bd2d48ae0c2f1fa51ab1c3d <details><summary>spanner: 1.87.0</summary> ## [1.87.0](spanner/v1.86.1...spanner/v1.87.0) (2025-12-10) ### Features * Add Send and Ack mutations for Queues (PiperOrigin-RevId: 832425466) ([185951b](185951b3)) * Exposing AutoscalingConfig in InstancePartition (PiperOrigin-RevId: 825184314) ([185951b](185951b3)) * Add Spanner location API (PiperOrigin-RevId: 833474957) ([185951b](185951b3)) * Add QueryAdvisorResult for query plan (PiperOrigin-RevId: 832425466) ([185951b](185951b3)) * improve the SQL formatting when printing out SQL (#13267) ([af0806f](af0806f4)) * Add grpc.xds.resource_type label to xDS client metrics (#13358) ([b9196cf](b9196cf6)) * support subquery in View Join (#13266) ([d19f797](d19f797b)) ### Bug Fixes * add env var to allow disabling directpath bound token (#13265) ([029bc79](029bc795)) * fix createMultiplexedSession goroutine leak (#13396) ([1805e89](1805e895)) * decoding spanner rows using SelectAll should map values in correct annotations (#13301) ([315f65b](315f65b5)) * error instead of panic for iterator after tx end (#13366) ([a27c19a](a27c19ae)) * transaction_tag should be set on BeginTransactionRequest (#13463) ([a429aea](a429aea4)) * Configure keepAlive time for gRPC TCP connections (#13216) ([ca8f64e](ca8f64e0)) * avoid double decrement in session counting (#13395) ([e036421](e0364214)) ### Documentation * minor update for Spanner Location API (PiperOrigin-RevId: 834841888) ([185951b](185951b3)) * Update description for the BatchCreateSessionsRequest and Session (PiperOrigin-RevId: 832425466) ([185951b](185951b3)) * Update description for the IsolationLevel (PiperOrigin-RevId: 832425466) ([185951b](185951b3)) </details>
…3464) PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v1.0.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/librarian-go@sha256:718167d5c23ed389b41f617b3a00ac839bdd938a6bd2d48ae0c2f1fa51ab1c3d <details><summary>spanner: 1.87.0</summary> ## [1.87.0](googleapis/google-cloud-go@spanner/v1.86.1...spanner/v1.87.0) (2025-12-10) ### Features * Add Send and Ack mutations for Queues (PiperOrigin-RevId: 832425466) ([185951b](googleapis@185951b3)) * Exposing AutoscalingConfig in InstancePartition (PiperOrigin-RevId: 825184314) ([185951b](googleapis@185951b3)) * Add Spanner location API (PiperOrigin-RevId: 833474957) ([185951b](googleapis@185951b3)) * Add QueryAdvisorResult for query plan (PiperOrigin-RevId: 832425466) ([185951b](googleapis@185951b3)) * improve the SQL formatting when printing out SQL (googleapis#13267) ([af0806f](googleapis@af0806f4)) * Add grpc.xds.resource_type label to xDS client metrics (googleapis#13358) ([b9196cf](googleapis@b9196cf6)) * support subquery in View Join (googleapis#13266) ([d19f797](googleapis@d19f797b)) ### Bug Fixes * add env var to allow disabling directpath bound token (googleapis#13265) ([029bc79](googleapis@029bc795)) * fix createMultiplexedSession goroutine leak (googleapis#13396) ([1805e89](googleapis@1805e895)) * decoding spanner rows using SelectAll should map values in correct annotations (googleapis#13301) ([315f65b](googleapis@315f65b5)) * error instead of panic for iterator after tx end (googleapis#13366) ([a27c19a](googleapis@a27c19ae)) * transaction_tag should be set on BeginTransactionRequest (googleapis#13463) ([a429aea](googleapis@a429aea4)) * Configure keepAlive time for gRPC TCP connections (googleapis#13216) ([ca8f64e](googleapis@ca8f64e0)) * avoid double decrement in session counting (googleapis#13395) ([e036421](googleapis@e0364214)) ### Documentation * minor update for Spanner Location API (PiperOrigin-RevId: 834841888) ([185951b](googleapis@185951b3)) * Update description for the BatchCreateSessionsRequest and Session (PiperOrigin-RevId: 832425466) ([185951b](googleapis@185951b3)) * Update description for the IsolationLevel (PiperOrigin-RevId: 832425466) ([185951b](googleapis@185951b3)) </details>
Fix: #13299