From 2a99e5575674e322f8cf29613d301b94c9db925f Mon Sep 17 00:00:00 2001 From: vomba Date: Wed, 3 Jun 2026 14:46:06 +0200 Subject: [PATCH] fix: preserve correct parent references in explode merge anchor reconstruction When explode resolves merge anchors (<<:), items copied from the alias target retained the original parent pointer instead of being set to the enclosing node being reconstructed. This caused GetPath() to return wrong paths for children of merge-anchored nodes, making subsequent merge operations target the wrong LHS keys. In fixedReconstructAliasedMap, set copied.Parent = node after copy. In reconstructAliasedMap (non-spec path), replace AddChild (which creates sequence-style numeric-index entries) with AddKeyValueChild to properly reconstruct mapping key-value pairs. AddKeyValueChild also correctly sets parent references via SetParent. --- pkg/yqlib/operator_anchors_aliases.go | 10 ++++++++-- pkg/yqlib/operator_anchors_aliases_test.go | 23 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/pkg/yqlib/operator_anchors_aliases.go b/pkg/yqlib/operator_anchors_aliases.go index ddff9f73..75768438 100644 --- a/pkg/yqlib/operator_anchors_aliases.go +++ b/pkg/yqlib/operator_anchors_aliases.go @@ -183,7 +183,9 @@ func fixedReconstructAliasedMap(node *CandidateNode) error { }) for _, item := range itemsToAdd { - newContent = append(newContent, item.Copy()) + copied := item.Copy() + copied.Parent = node + newContent = append(newContent, copied) } } } @@ -226,8 +228,12 @@ func reconstructAliasedMap(node *CandidateNode, context Context) error { } } node.Content = make([]*CandidateNode, 0) + entries := make([]*CandidateNode, 0, newContent.Len()) for newEl := newContent.Front(); newEl != nil; newEl = newEl.Next() { - node.AddChild(newEl.Value.(*CandidateNode)) + entries = append(entries, newEl.Value.(*CandidateNode)) + } + for i := 0; i < len(entries); i += 2 { + node.AddKeyValueChild(entries[i], entries[i+1]) } return nil } diff --git a/pkg/yqlib/operator_anchors_aliases_test.go b/pkg/yqlib/operator_anchors_aliases_test.go index 9d9c5ed6..df168571 100644 --- a/pkg/yqlib/operator_anchors_aliases_test.go +++ b/pkg/yqlib/operator_anchors_aliases_test.go @@ -234,6 +234,29 @@ var fixedAnchorOperatorScenarios = []expressionScenario{ "D0, P[], (!!map)::a:\n b: 42\nb: 42\n", }, }, + { + skipDoc: true, + description: "Merge after explode preserves correct parent references", + document: `opensearch: &opensearch-cluster + ip2geo: + enabled: false + +opensearch-client: + <<: *opensearch-cluster + nodeGroup: client + opensearchJavaOpts: "-Xmx1024m -Xms1024m"`, + document2: `opensearch: &opensearch-cluster + ip2geo: + enabled: true + +opensearch-client: + <<: *opensearch-cluster + opensearchJavaOpts: "-Xmx1536m -Xms1536m"`, + expression: `(select(fi == 0) | explode(.)) * (select(fi == 1) | explode(.))`, + expected: []string{ + "D0, P[], (!!map)::opensearch:\n ip2geo:\n enabled: true\nopensearch-client:\n ip2geo:\n enabled: true\n nodeGroup: client\n opensearchJavaOpts: \"-Xmx1536m -Xms1536m\"\n", + }, + }, } var badAnchorOperatorScenarios = []expressionScenario{