Skip to content

fix: serialize Spanner JSON floats with a pure Ruby formatter - #433

Open
Connorhd wants to merge 1 commit into
googleapis:mainfrom
indie-technologies:codex/upstream-spanner-json-floats
Open

Connorhd wants to merge 1 commit into
googleapis:mainfrom
indie-technologies:codex/upstream-spanner-json-floats

Conversation

@Connorhd

Copy link
Copy Markdown
Contributor

This is a pretty awkward one, but spanner requires floats in JSON values are serialized the exact way spanner would internally serialize them (using nlohmann/json). This PR implements ports the implementation of the Grisu2 algorithm from that library to ruby, and adds a custom JSON spanner type to make sure any JSON serialized by activerecord for spanner uses that serialization.

Without this change certain float values will cause queries to fail (1.240425 is an example).

Encode floating-point values in Spanner JSON using a Ruby port of Nlohmann's Grisu2 algorithm. This produces the decimal representations expected by Spanner's precision checks while keeping the formatting scoped to Spanner JSON values.

Cover JSON attributes and arrays, query parameters, and SDK mutations, with exact-output fixtures and serialization isolation tests.

Co-authored-by: Codex <codex@openai.com>
@product-auto-label product-auto-label Bot added the api: spanner Issues related to the googleapis/ruby-spanner-activerecord API. label Sep 28, 2026
@google-cla

google-cla Bot commented Sep 28, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

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 introduces a pure Ruby port of the Grisu2 algorithm from nlohmann/json 3.11.3 to format finite floats before encoding JSON documents, ensuring decimal precision is preserved during round-trips with Google Spanner. It integrates this formatter into a custom ActiveRecord JSON type and prepends it to the Google Cloud Spanner client conversion logic. The feedback highlights a critical compatibility issue in FloatFormatter where appending an integer directly to a string using << will raise a TypeError in Ruby 3.2 and later, and suggests using .chr to convert the character codes first.


while length.positive?
digit, integral = integral.divmod power_of_ten
digits << (48 + digit)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In Ruby 3.2 and later, passing an integer to String#<< raises a TypeError (e.g., no implicit conversion of Integer into String). Since the gemspec specifies required_ruby_version = ">= 3.2", this will cause a runtime crash when formatting floats. Converting the character code to a string using .chr resolves this issue.

          digits << (48 + digit).chr

fraction *= 10
digit = fraction >> shift
fraction &= one - 1
digits << (48 + digit)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

In Ruby 3.2 and later, passing an integer to String#<< raises a TypeError (e.g., no implicit conversion of Integer into String). Since the gemspec specifies required_ruby_version = ">= 3.2", this will cause a runtime crash when formatting floats. Converting the character code to a string using .chr resolves this issue.

          digits << (48 + digit).chr

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 googleapis/ruby-spanner-activerecord API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant