From 5340ed0ad3e3332138e1726a000d218ae3af4959 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Sun, 16 May 2021 12:16:10 +1000 Subject: [PATCH] Fixed handling of null expressions in equals op --- pkg/yqlib/operator_equals.go | 20 ++++++++++++++++---- pkg/yqlib/operator_equals_test.go | 22 ++++++++++++++++++++++ pkg/yqlib/operator_select.go | 2 ++ pkg/yqlib/operators.go | 23 +++++++++++++---------- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/pkg/yqlib/operator_equals.go b/pkg/yqlib/operator_equals.go index fe465eaf..157baf4d 100644 --- a/pkg/yqlib/operator_equals.go +++ b/pkg/yqlib/operator_equals.go @@ -10,14 +10,26 @@ func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *Expre func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { return func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) { value := false - + log.Debugf("-- isEquals cross function") if lhs == nil && rhs == nil { owner := &CandidateNode{} return createBooleanCandidate(owner, !flip), nil } else if lhs == nil { - return createBooleanCandidate(rhs, flip), nil + log.Debugf("lhs nil, but rhs is not") + rhsNode := unwrapDoc(rhs.Node) + value := rhsNode.Tag == "!!null" + if flip { + value = !value + } + return createBooleanCandidate(rhs, value), nil } else if rhs == nil { - return createBooleanCandidate(lhs, flip), nil + log.Debugf("lhs not nil, but rhs is") + lhsNode := unwrapDoc(lhs.Node) + value := lhsNode.Tag == "!!null" + if flip { + value = !value + } + return createBooleanCandidate(lhs, value), nil } lhsNode := unwrapDoc(lhs.Node) @@ -37,6 +49,6 @@ func isEquals(flip bool) func(d *dataTreeNavigator, context Context, lhs *Candid } func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { - log.Debugf("-- equalsOperation") + log.Debugf("-- notEqualsOperator") return crossFunction(d, context, expressionNode, isEquals(true), true) } diff --git a/pkg/yqlib/operator_equals_test.go b/pkg/yqlib/operator_equals_test.go index 734f0872..2741fdfc 100644 --- a/pkg/yqlib/operator_equals_test.go +++ b/pkg/yqlib/operator_equals_test.go @@ -14,6 +14,28 @@ var equalsOperatorScenarios = []expressionScenario{ "D0, P[], (!!bool)::false\n", }, }, + { + skipDoc: true, + document: "{a: {b: 10}}", + expression: "select(.c != null)", + expected: []string{}, + }, + { + skipDoc: true, + document: "{a: {b: 10}}", + expression: "select(.d == .c)", + expected: []string{ + "D0, P[], (doc)::{a: {b: 10}}\n", + }, + }, + { + skipDoc: true, + document: "{a: {b: 10}}", + expression: "select(null == .c)", + expected: []string{ + "D0, P[], (doc)::{a: {b: 10}}\n", + }, + }, { skipDoc: true, document: "{a: { b: {things: \"\"}, f: [1], g: [] }}", diff --git a/pkg/yqlib/operator_select.go b/pkg/yqlib/operator_select.go index 5c659e8f..c89cbf4d 100644 --- a/pkg/yqlib/operator_select.go +++ b/pkg/yqlib/operator_select.go @@ -24,7 +24,9 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre if first != nil { result := first.Value.(*CandidateNode) + log.Debugf("result %v", NodeToString(result)) includeResult, errDecoding := isTruthy(result) + log.Debugf("isTruthy %v", includeResult) if errDecoding != nil { return Context{}, errDecoding } diff --git a/pkg/yqlib/operators.go b/pkg/yqlib/operators.go index 0a0e25dc..1242b6f4 100644 --- a/pkg/yqlib/operators.go +++ b/pkg/yqlib/operators.go @@ -24,7 +24,17 @@ func emptyOperator(d *dataTreeNavigator, context Context, expressionNode *Expres type crossFunctionCalculation func(d *dataTreeNavigator, context Context, lhs *CandidateNode, rhs *CandidateNode) (*CandidateNode, error) -func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List) error { +func resultsForRhs(d *dataTreeNavigator, context Context, lhsCandidate *CandidateNode, rhs Context, calculation crossFunctionCalculation, results *list.List, calcWhenEmpty bool) error { + + if calcWhenEmpty && rhs.MatchingNodes.Len() == 0 { + resultCandidate, err := calculation(d, context, lhsCandidate, nil) + if err != nil { + return err + } + results.PushBack(resultCandidate) + return nil + } + for rightEl := rhs.MatchingNodes.Front(); rightEl != nil; rightEl = rightEl.Next() { log.Debugf("Applying calc") rhsCandidate := rightEl.Value.(*CandidateNode) @@ -52,14 +62,7 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi } if calcWhenEmpty && lhs.MatchingNodes.Len() == 0 { - if rhs.MatchingNodes.Len() == 0 { - resultCandidate, err := calculation(d, context, nil, nil) - if err != nil { - return Context{}, err - } - results.PushBack(resultCandidate) - } - err := resultsForRhs(d, context, nil, rhs, calculation, results) + err := resultsForRhs(d, context, nil, rhs, calculation, results, calcWhenEmpty) if err != nil { return Context{}, err } @@ -68,7 +71,7 @@ func doCrossFunc(d *dataTreeNavigator, context Context, expressionNode *Expressi for el := lhs.MatchingNodes.Front(); el != nil; el = el.Next() { lhsCandidate := el.Value.(*CandidateNode) - err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results) + err := resultsForRhs(d, context, lhsCandidate, rhs, calculation, results, calcWhenEmpty) if err != nil { return Context{}, err }