From 88bfbec97b77703efc8a1151789131075d5dfc62 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Thu, 12 Jun 2025 14:25:05 +1000 Subject: [PATCH] Fix add op when there are not matches piped in, #2383, #2384 --- pkg/yqlib/expression_parser.go | 6 +++++ pkg/yqlib/operator_add.go | 5 +++- pkg/yqlib/operator_add_test.go | 45 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/yqlib/expression_parser.go b/pkg/yqlib/expression_parser.go index 622fb416..399ef9c7 100644 --- a/pkg/yqlib/expression_parser.go +++ b/pkg/yqlib/expression_parser.go @@ -9,6 +9,7 @@ type ExpressionNode struct { Operation *Operation LHS *ExpressionNode RHS *ExpressionNode + Parent *ExpressionNode } type ExpressionParserInterface interface { @@ -57,6 +58,7 @@ func (p *expressionParserImpl) createExpressionTree(postFixPath []*Operation) (* } remaining, rhs := stack[:len(stack)-1], stack[len(stack)-1] newNode.RHS = rhs + rhs.Parent = &newNode stack = remaining case 2: if len(stack) < 2 { @@ -64,7 +66,11 @@ func (p *expressionParserImpl) createExpressionTree(postFixPath []*Operation) (* } remaining, lhs, rhs := stack[:len(stack)-2], stack[len(stack)-2], stack[len(stack)-1] newNode.LHS = lhs + lhs.Parent = &newNode + newNode.RHS = rhs + rhs.Parent = &newNode + stack = remaining } } diff --git a/pkg/yqlib/operator_add.go b/pkg/yqlib/operator_add.go index 044fe4a0..37be8f81 100644 --- a/pkg/yqlib/operator_add.go +++ b/pkg/yqlib/operator_add.go @@ -38,8 +38,11 @@ func toNodes(candidate *CandidateNode, lhs *CandidateNode) []*CandidateNode { func addOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { log.Debugf("Add operator") + // if we don't have any matching nodes, but we were piped into (have a parent exp?) then we + // shouldn't calcWhenEmpt + calcWhenEmpty := expressionNode.Parent == nil - return crossFunction(d, context.ReadOnlyClone(), expressionNode, add, true) + return crossFunction(d, context.ReadOnlyClone(), expressionNode, add, calcWhenEmpty) } func add(_ *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { diff --git a/pkg/yqlib/operator_add_test.go b/pkg/yqlib/operator_add_test.go index 5b1b773d..92ed3d96 100644 --- a/pkg/yqlib/operator_add_test.go +++ b/pkg/yqlib/operator_add_test.go @@ -5,6 +5,51 @@ import ( ) var addOperatorScenarios = []expressionScenario{ + { + skipDoc: true, + expression: `"foo" + "bar"`, + expected: []string{ + "D0, P[], (!!str)::foobar\n", + }, + }, + { + skipDoc: true, + expression: `[] | .[] | "foo" + .`, + expected: []string{}, + }, + { + skipDoc: true, + expression: `[] | .[] | . + "foo"`, + expected: []string{}, + }, + { + skipDoc: true, + expression: `select(.) | "foo" + "bar"`, + expected: []string{ + "D0, P[], (!!str)::foobar\n", // jq does not do this :/ - but yq has for quite some time. + }, + }, + { + skipDoc: true, + document: "apples: 3", + expression: `.apples + 3`, + expected: []string{ + "D0, P[apples], (!!int)::6\n", + }, + }, + { + skipDoc: true, + document: "apples: 3", + expression: `.bobo + 3`, + expected: []string{ + "D0, P[], (!!int)::3\n", + }, + }, + { + skipDoc: true, + expression: `select(.) | "cat" + .`, + expected: []string{}, + }, { skipDoc: true, document: `[{a: foo, b: bar}, {a: 1, b: 2}]`,