[BugFix] Shadow stale mapped leaves when an override replaces an object parent (#5718) - #5726
[BugFix] Shadow stale mapped leaves when an override replaces an object parent (#5718)#5726Ystk-hsn wants to merge 2 commits into
Conversation
PR Reviewer Guide 🔍(Review updated until commit 50a90d5)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 50a90d5 Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit c5c745d
Suggestions up to commit a29ce5b
Suggestions up to commit 20a0604
|
|
The 6 failing unit jobs all hit the |
Hi @Ystk-hsn , I think you need to rebase to include the fix from main if you haven't. |
…ct parent (opensearch-project#5718) When spath (or any command funnelling through projectPlusOverriding) assigns to a name that collides with a mapped object field, the exact-name override replaced only the struct-parent column and left the flattened leaf columns (log.level, log.src) in the row schema. QualifiedNameResolver prefers an exact-name column, so leaf references silently answered from the stale mapping instead of the extracted value. Mirror the existing dropStructParentsFor step: when the replaced column was container-typed (MAP object parent / ARRAY nested parent), drop its flattened leaf columns so the replacement shadows the entire subtree. The type gate keeps user-created literal dotted columns (eval x.y = 1) independent of scalar prefix overrides, preserving the SPL1 semantics restored in PR opensearch-project#5351. Also fixes the companion defect where a post-spath eval on a stale leaf fired dropStructParentsFor against the freshly extracted map (Field [log] not found). Document the collision behaviour in docs/user/ppl/cmd/spath.md. Signed-off-by: Yasutaka Hisano <yasutennis713@gmail.com>
20a0604 to
a29ce5b
Compare
|
Persistent review updated to latest commit a29ce5b |
|
Thanks @dai-chen — rebased onto latest main. All tests pass locally on the rebased branch. A review would also be much appreciated. |
| } | ||
|
|
||
| /** An OpenSearch object parent surfaces as MAP in the row schema, a nested parent as ARRAY. */ | ||
| private static boolean isContainerType(RelDataType type) { |
There was a problem hiding this comment.
The gate is a pure type test, so any MAP/ARRAY column counts as a mapped parent, including ones a function just built, which can't have stale flattened leaves. Could you check are both of these returning a row on main and 400 with the changes?
... | eval arr = array(1,2) | eval `arr.x` = 5 | eval arr = array(3,4) | fields arr, `arr.x`
main → [[3,4], 5] → Field [arr.x] not found?
... | spath input=body output=data | eval `data.custom` = 'kept'
| spath input=body output=data | fields data, `data.custom`
main → (map, "kept") → Field [data.custom] not found?
There was a problem hiding this comment.
Thanks for the fb.
Confirmed, both queries returned 400 with the previous gate. The gate now checks for the exact type shape the schema conversion produces for mapping-derived parents, which is MAP(VARCHAR, ANY) for object parents and ARRAY(ANY) for nested. Function-built containers carry concrete value/element types, so they no longer trigger the pruning. Both of your scenarios are added to CalcitePPLSpathCollisionIT and now return the same rows as main.
One thing I would like your take on. This is still a type shape heuristic rather than real provenance, so any function whose return type shares that shape is treated as a mapping-derived parent. Scanning the registered functions, map_append is currently the one such case, and the pattern needed to hit it is quite contrived, so I think the heuristic is acceptable for this PR. If a stricter mechanism is needed, it looks like a substantially bigger change, so having a direction from you would be appreciated before I take it on, here or as a follow-up.
There was a problem hiding this comment.
Thanks for confirming! If I understand correct, this may be caused by the "parent-child info" missing in our symbol table. If we cannot support such edge case anyway, I think you previous revision by simple type check on parent field is more straightforward.
|
Persistent review updated to latest commit c5c745d |
- Gate the stale-leaf pruning on the exact type shape the OpenSearch schema conversion produces (MAP(VARCHAR, ANY) object parent / ARRAY(ANY) nested parent) instead of any MAP/ARRAY. Function-built containers (array(...), a previous spath result) carry concrete value/element types, cannot have stale flattened leaves, and no longer trigger the pruning — fixes the two reviewer scenarios where a literal dotted column was destroyed. - Run the pruning before any new columns are added (on the original row), so the prefix match can never observe the incoming newNames after rename. - Add both reviewer scenarios to CalcitePPLSpathCollisionIT and a yaml rest test (issues/5718.yml) covering the core collision cases. Signed-off-by: Yasutaka Hisano <yasutennis713@gmail.com>
c5c745d to
50a90d5
Compare
|
Persistent review updated to latest commit 50a90d5 |
|
@Ystk-hsn Replied to your comments. Please check the CI failure. Thanks! |
Description
Problem
spath input=body output=logon an index wherelogis a mapped object field silently answerslog.<key>references from the stale mapped leaves instead of the extracted JSON (issue #5718). Within a single row, each leaf independently reads whichever source happens to be mapped — andwhere log.level = 'ERROR'on the extracted value silently matches nothing.Root cause
Three interacting behaviors:
log) plus flattened leaf columns (log.level,log.src) side by side.projectPlusOverridingreplaces only exact-name matches, sospath's rewrite (eval log = json_extract_all(body)) replaces the parent column but leaves the stale leaf columns in the schema.QualifiedNameResolverprefers an exact-name column over map access, solog.levelresolves to the stale leaf whilelog.msg(unmapped) falls through to the fresh map.Fix
Add the mirror of the existing
dropStructParentsForstep: when an override replaces a column that was an object/nested parent (MAP/ARRAY-typed), also drop its flattened leaf columns (dropStructChildrenFor). The replacement value then shadows the entire<name>.*subtree — issue #5718's preferred option 1.The children-drop is type-gated on the replaced column being container-typed. A scalar column that merely shares a dotted prefix with user-created literal columns (
eval x.y = 1 | eval x = 2) keeps its children, preserving the SPL1 literal-field semantics that PR #5351 restored. Same gating style as the existing parent-drop: it only fires when the override actually replaced a container column.This also fixes a companion defect found while reproducing: with stale leaves present, a subsequent
eval log.level = 'patched'fired the override path anddropStructParentsForremoved the freshly extracted map (Field [log] not found). With the stale leaves gone, the assignment creates a literal column and the parent survives, consistent with the #5185 semantics.Behavior changes (only when an assignment collides with a mapped object/nested parent)
spath output=<object parent>→ leaf referencewhereon leafnullspath ... path=.../eval <parent> = <scalar>→ leaf referencespathcollision →evala dotted leafField not founderrorDefault (
fields *) output shape is unchanged — the stale leaves were already hidden bytryToRemoveNestedFields; they were only reachable by explicit reference.Known accepted corner: a literal dotted column created under a mapped object parent is dropped together with the mapped leaves when the parent itself is overridden (provenance of individual columns is not tracked).
Testing
CalcitePPLSpathCollisionIT(10 tests) written against the expected semantics before the fix: 7 failed on the unfixed build reproducing every symptom (A/B/C and path-mode) plus the companion defect; 3 guard tests pinned the already-correct behaviors. All 10 pass with the fix.main(issue was reported against3.8; confirms main is affected).:ppl:testspath units,:core:test,:integ-test:yamlRestTest(includes theissues/5185.ymlregression).docs/user/ppl/cmd/spath.md(prose only; no new doctest blocks).Related Issues
Resolves #5718
Check List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.