feat(spanner): implement FromValue for serde_json::Value with dynamic STRUCT/ARRAY deserialization#5863
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements the FromValue trait for serde_json::Value (JsonValue), enabling dynamic deserialization of Spanner values (including nested arrays and structs) into JSON using runtime type metadata. It also adds helper methods to the Type struct and introduces comprehensive unit tests. The review feedback suggests refactoring several if/else blocks to reduce code duplication and avoid unnecessary cloning of field names, as well as extracting repeated array conversion logic into a helper function to adhere to the DRY principle.
… STRUCT/ARRAY deserialization Any query returning STRUCT or ARRAY<STRUCT> columns (partition queries, change stream TVFs, INFORMATION_SCHEMA) could not be deserialized without a predefined Rust type. The existing FromValue trait only supported compile-time-known types, making dynamic data extraction (e.g. RisingWave reading arbitrary Spanner tables) impossible without manual Value tree-walking. Added impl FromValue for serde_json::Value that recursively converts any Spanner Value into serde_json::Value using runtime Type metadata. Key mechanisms: - Positional ListValue + StructType metadata → named JSON object (the core reconstruction). Missing positional fields insert null rather than being silently omitted. - Named StructValue → JSON object via Struct::from_ref. Missing metadata-declared fields also insert null for consistency. - TypeCode::Json columns → parsed from wire StringValue into structured JSON. - NaN/Infinity → JsonValue::Null (matches SDK into_serde_value convention). Callers cannot distinguish these from genuine SQL NULLs. - Duplicate field names (e.g. unnamed SELECT expressions) use last-write-wins via serde_json::Map::insert. - Recursion depth limit (MAX_RECURSION_DEPTH = 64) prevents stack overflow on pathological inputs. - Type::struct_type() accessor for zero-cost metadata access. - Type::from_ref() zero-cost cast (matches Value::from_ref pattern) eliminating per-field Type clones during recursive deserialization.
b446097 to
4996bf9
Compare
|
/gcbrun |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements the FromValue trait for serde_json::Value to enable dynamic deserialization of Spanner values (including STRUCT, ARRAY, and JSON columns) using runtime type metadata, accompanied by comprehensive unit tests. The review feedback suggests key efficiency and maintainability improvements in the recursive conversion logic, such as avoiding redundant allocations of Type::default(), refactoring the signature to use Option<&Type>, and replacing k.to_string() with k.clone() to avoid unnecessary formatting overhead.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5863 +/- ##
==========================================
+ Coverage 97.89% 97.91% +0.01%
==========================================
Files 226 226
Lines 57808 58581 +773
==========================================
+ Hits 56592 57358 +766
- Misses 1216 1223 +7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@buu-nguyen I opened thealtoclef#2 to address the formatting issues + some other minor concerns that I had with this implementation. Would you mind taking a look at that and merge that into your pull request? |
- Fixes the formatting errors in the PR - Adds more tests, including an integration test - Splits the method into multiple smaller steps for better readability - Fixes an issue with NaN and Inf values for float64 and float32
It's merged. Can you re-run the test suite? Thanks |
|
/gcbrun |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements dynamic deserialization of Spanner values into serde_json::Value using runtime type metadata, along with comprehensive unit and integration tests. The code review feedback focuses on aligning the implementation with the repository's style guide. Specifically, it recommends using the // SAFETY: prefix (in all caps) for the unsafe block in types.rs, simplifying a match arm in from_value.rs using Option::map and unwrap_or, and applying the 'Return Early' principle via let else statements to reduce nesting and eliminate unnecessary else blocks.
|
@buu-nguyen I forgot about these warnings in my changes: Would you mind taking a look at those? (Can you view the build log?) And also take a look at the last comments from gemini? |
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…let and else block, making the code more concise and idiomatic
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
69c173d to
79c5ef3
Compare
…stant lint The Float64-to-JSON test used 3.14f64, which clippy flags as an approximation of f64::consts::PI when running with --tests and -D clippy::approx_constant. This lint is enforced in CI. Changed the test value to 1.5, which is not an approximation of any well-known mathematical constant.
|
@olavloite I did apply suggestions from Gemini. Also updated the test to resolve the |
|
/gcbrun |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements the FromValue trait for serde_json::Value, enabling dynamic deserialization of Spanner values (including nested arrays, structs, and JSON columns) using runtime type metadata. It also adds helper methods to Type and comprehensive unit and integration tests. The review feedback suggests that parsing failures for Float64 and Float32 types when represented as StringValue should not be silently ignored, but rather propagated as a ConvertError to adhere to the "Demand Explosive Correctness" principle.
|
/gcbrun |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements the FromValue trait for serde_json::Value, enabling dynamic deserialization of Spanner values (including STRUCT, ARRAY, and nested types) into JSON values using runtime type metadata. It also adds helper methods to Type for struct metadata and safe reference reinterpretation, along with comprehensive unit and integration tests. The feedback suggests refactoring the float parsing logic in from_value_recursive to return early, thereby avoiding an unnecessary else block and reducing nesting depth in accordance with the repository style guide.
… logic flow linear
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request implements the FromValue trait for serde_json::Value to enable dynamic deserialization of Spanner types (such as STRUCT, ARRAY, and JSON) into JSON without requiring predefined Rust types. It introduces recursive conversion helpers, handles Spanner-specific type mappings (such as preserving high-precision types as strings and converting non-finite floats to null), and adds comprehensive unit and integration tests. Additionally, helper methods were added to Type to retrieve struct metadata and safely cast references. No review comments were provided, and the implementation is clean, well-tested, and conforms to the repository's style guidelines.
Gemini finally approved this. |
|
/gcbrun |
Summary
Implements
FromValue for serde_json::Valueto enable dynamic deserialization of all Spanner column types — including STRUCT, ARRAY, and nestedARRAY<STRUCT<ARRAY<...>>>— without requiring predefined Rust structs.This unblocks:
Usage:
Resolves #5860