Repository navigation
fix(expression): return a parse error for non-string type nodes in expression JSON - #982
LuciferYang wants to merge 1 commit into
Conversation
… serde IsTransformTerm, NamedReferenceFromJson, both LiteralFromJson overloads, and ExpressionFromJson read the 'type' discriminator with json[kType].get<std::string>() guarded only by contains(), so a present-but-non-string 'type' (e.g. a number) threw an uncaught nlohmann type_error out of the Result-returning parse API. Check is_string() first, matching the sibling OperationTypeFromJson guard, so malformed expression JSON yields JsonParseError instead of an escaped exception.
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The guards comprehensively address the exception paths with focused regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Prevents malformed expression JSON from throwing exceptions outside the Result contract.
Changes:
- Validates
"type"and"term"nodes before string conversion. - Adds regression coverage for all six affected parsing paths.
| File | Description |
|---|---|
src/iceberg/expression/json_serde.cc |
Guards string conversions and returns parse errors. |
src/iceberg/test/expression_json_test.cc |
Tests non-string discriminator handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
advancedxy
left a comment
There was a problem hiding this comment.
LGTM.
Agent alerted me there's one other similar failure issue during the code review:
iceberg-cpp/src/iceberg/json_serde.cc
Lines 496 to 505 in b4eecba
json[kElement] directly without check its existence, which also triggers SIGABRT. It should follow the MapTypeFromJson's practice. would you mind to submit a follow-up pr as well?
Yes, will fix in a separate PR. |
|
@advancedxy Opened #997 for |
What
Expression JSON deserialization in
src/iceberg/expression/json_serde.cccalledjson[kType].get<std::string>()(and onejson[kTerm].get<std::string>()) guarded only byis_object()andcontains(kType). A non-string"type"/"term"made nlohmann throwjson::type_error.302, which escaped theseResult-returning functions (the expression parse chain has notry/catch) and terminated the caller instead of returning an error. This is the same failure mode as the merged #857.It is reachable, not test-only. Table-metadata parsing hits the type-aware
LiteralFromJsonthroughFieldFromJson'sinitial-default/write-defaulthandling, and REST responses hit the expression parsers through the scan-metrics report filter and the residual/partition/plan filters, all viaICEBERG_ASSIGN_OR_RAISE, which forwards aResulterror but not a thrown exception.Closes #979.
How
Each of the six
get<std::string>()sites now checksis_string()first and returnsJsonParseErroron a non-string node, mirroring the siblingOperationTypeFromJson. Valid string input is unaffected: the addedis_string()sits in a short-circuit&&, so a well-formed node evaluates exactly as before, and the guard only rejects input that previously threw.Testing
NonStringTypeIsParseErrorinexpression_json_test.cccovers all six guards: a non-string"type"at the top level (ExpressionFromJson), on an and/or node, on a predicate's term node (routed through the transform-term check and then the named-reference parser), a non-string reference"term", and a non-string"type"on both the untyped and the type-awareLiteralFromJsonoverloads. Each case returnskJsonParseError; without the fix the corresponding input throwsjson::type_error.302out of theResult-returning function. Removing any single guard makes one of these cases throw, so no guard is left unpinned.