Skip to content

Conversation

@rahul2393
Copy link
Contributor

Fix: #13299

@rahul2393 rahul2393 requested review from a team as code owners November 5, 2025 16:19
@rahul2393 rahul2393 changed the title fix: assign value to correct index if columns are out of order in SelectAll fix(spanner): assign value to correct index if columns are out of order in SelectAll Nov 5, 2025
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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 bug in the SelectAll function within the Spanner client library. Previously, when querying data into a slice of structs, the function would incorrectly map column values to struct fields if the order of columns returned by the database did not match the declaration order of fields in the Go struct. The fix introduces a robust mechanism to identify and use the correct struct field index for each database column, ensuring that data is assigned accurately regardless of the column order from the query result.

Highlights

  • Corrected Column-to-Field Mapping: The SelectAll function now correctly assigns values to struct fields even when the database columns are returned in a different order than the struct's field definition.
  • Introduction of Field Index Tracking: A new fieldIndexes slice is introduced to store the correct Go struct field index for each corresponding database column, ensuring accurate data assignment.
  • Enhanced structPointers and initFieldTag: These helper functions were updated to return and utilize the fieldIndexes map, facilitating the new mapping logic.
  • New Test Case for Column Order: A dedicated test case was added to spanner/row_test.go to explicitly verify the fix for out-of-order columns.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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 an issue where SelectAll would fail to decode data into a struct if the column order in the result set did not match the struct's field order. The change introduces a mapping from column index to struct field index, which resolves the primary problem for flat structs, and a good test case has been added to verify this.

However, I've found a couple of issues with the implementation that could lead to incorrect behavior or panics in more complex scenarios, specifically with structs that have embedded fields, and in lenient mode. My review comments provide more details on these issues.

Comment on lines 545 to 550
} else {
if !lenient {
return nil, errNoOrDupGoField(sliceItem, colName.GetName())
return nil, nil, errNoOrDupGoField(sliceItem, colName.GetName())
}
fieldVal = sliceItem.FieldByName(colName.GetName())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This fallback logic for lenient mode has a flaw. If sliceItem.FieldByName successfully finds a field (which could happen for fields in embedded structs that initFieldTag might not handle correctly), the fieldIdx is not updated. It retains its initial value of i (the column index), which leads to fieldIndexes[i] being set to i. This bypasses the column-to-field-index mapping for this field, leading to incorrect data assignment if columns are out of order.

Given that initFieldTag is intended to populate the maps for all relevant fields, this else block should ideally handle columns that are not present in the destination struct. In lenient mode, these should be ignored. The current code does lead to them being ignored because FieldByName would likely fail and result in an invalid fieldVal. However, if FieldByName succeeds, it introduces a bug.

To make this safer, you should either ensure this path also correctly resolves the field index, or rely solely on the pre-computed maps from initFieldTag and treat any column not in the maps as a field to be ignored in lenient mode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embedded structs are not supported (see the recently added test cases). So this flaw can be ignored, as it is something that was not supported in the first place.

if fieldType.Anonymous {
sliceItemOfAnonymous := sliceItem.Field(i)
initFieldTag(sliceItemOfAnonymous, fieldTagMap)
initFieldTag(sliceItemOfAnonymous, fieldTagMap, fieldIndexes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The recursive call to initFieldTag for anonymous embedded structs will cause incorrect field indices to be stored in the fieldIndexes map. Inside the recursive call, the field index i is relative to the embedded struct (sliceItemOfAnonymous), not the top-level struct. However, the decoding in SelectAll uses e.Field(fieldIndexes[idx]), which expects an index on the top-level struct element e.

This will lead to panics or incorrect field assignments when decoding into structs with embedded fields.

A correct implementation would need to build an index path ([]int) for nested fields and use FieldByIndex for value access, which would be a more significant change throughout the decoding logic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embedded structs are not supported (see the recently added test cases). So this flaw can be ignored, as it is something that was not supported in the first place.

@rahul2393 rahul2393 requested a review from olavloite November 5, 2025 16:28
@product-auto-label product-auto-label bot added the api: spanner Issues related to the Spanner API. label Nov 6, 2025
olavloite added a commit that referenced this pull request Nov 14, 2025
Add some additional test cases for SelectAll() to verify that the behavior
remains the same if #13316
is merged.
rahul2393 pushed a commit that referenced this pull request Nov 14, 2025
Add some additional test cases for SelectAll() to verify that the
behavior remains the same if
#13316 is merged.
@rahul2393
Copy link
Contributor Author

@olavloite Should we go ahead with this one #13301, if yes then can you please help approve that

@rahul2393 rahul2393 closed this Nov 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the Spanner API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spanner: SelectAll with annotations is broken

2 participants