1
0
mirror of https://github.com/taigrr/yq synced 2025-01-18 04:53:17 -08:00

Fixing readonly ops not to modify context when paths dont exist

This commit is contained in:
Mike Farah 2021-05-16 14:00:30 +10:00
parent afebf0e621
commit 3f51a44596
12 changed files with 95 additions and 18 deletions

View File

@ -12,6 +12,14 @@ type Context struct {
DontAutoCreate bool DontAutoCreate bool
} }
func (n *Context) SingleReadonlyChildContext(candidate *CandidateNode) Context {
list := list.New()
list.PushBack(candidate)
newContext := n.ChildContext(list)
newContext.DontAutoCreate = true
return newContext
}
func (n *Context) SingleChildContext(candidate *CandidateNode) Context { func (n *Context) SingleChildContext(candidate *CandidateNode) Context {
list := list.New() list := list.New()
list.PushBack(candidate) list.PushBack(candidate)

View File

@ -20,9 +20,6 @@ type operationType struct {
Handler operatorHandler Handler operatorHandler
} }
// operators TODO:
// - mergeEmpty (sets only if the document is empty, do I do that now?)
var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator} var orOpType = &operationType{Type: "OR", NumArgs: 2, Precedence: 20, Handler: orOperator}
var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator} var andOpType = &operationType{Type: "AND", NumArgs: 2, Precedence: 20, Handler: andOperator}
var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator} var reduceOpType = &operationType{Type: "REDUCE", NumArgs: 2, Precedence: 35, Handler: reduceOperator}

View File

@ -71,7 +71,7 @@ func findBoolean(wantBool bool, d *dataTreeNavigator, context Context, expressio
if expressionNode != nil { if expressionNode != nil {
//need to evaluate the expression against the node //need to evaluate the expression against the node
candidate := &CandidateNode{Node: node} candidate := &CandidateNode{Node: node}
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode)
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@ -67,6 +67,22 @@ var booleanOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::a: true\nb: false\n", "D0, P[], (doc)::a: true\nb: false\n",
}, },
}, },
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `any_c(.name == "harry") as $c`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
},
},
{
skipDoc: true,
document: `[{pet: cat}]`,
expression: `all_c(.name == "harry") as $c`,
expected: []string{
"D0, P[], (doc)::[{pet: cat}]\n",
},
},
{ {
skipDoc: true, skipDoc: true,
document: `[false, false]`, document: `[false, false]`,

View File

@ -12,14 +12,21 @@ func hasOperator(d *dataTreeNavigator, context Context, expressionNode *Expressi
log.Debugf("-- hasOperation") log.Debugf("-- hasOperation")
var results = list.New() var results = list.New()
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) readonlyContext := context.Clone()
wanted := rhs.MatchingNodes.Front().Value.(*CandidateNode).Node readonlyContext.DontAutoCreate = true
wantedKey := wanted.Value rhs, err := d.GetMatchingNodes(readonlyContext, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
wantedKey := "null"
wanted := &yaml.Node{Tag: "!!null"}
if rhs.MatchingNodes.Len() != 0 {
wanted = rhs.MatchingNodes.Front().Value.(*CandidateNode).Node
wantedKey = wanted.Value
}
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)

View File

@ -13,6 +13,22 @@ var hasOperatorScenarios = []expressionScenario{
"D0, P[], (!!bool)::true\n", "D0, P[], (!!bool)::true\n",
}, },
}, },
{
skipDoc: true,
document: `a: hello`,
expression: `has(.b) as $c`,
expected: []string{
"D0, P[], (doc)::a: hello\n",
},
},
{
skipDoc: true,
document: `a: hello`,
expression: `has(.b)`,
expected: []string{
"D0, P[], (!!bool)::false\n",
},
},
{ {
description: "Has map key", description: "Has map key",
document: `- a: "yes" document: `- a: "yes"

View File

@ -11,9 +11,7 @@ func selectOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
childContext := context.SingleChildContext(candidate) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
childContext.DontAutoCreate = true
rhs, err := d.GetMatchingNodes(childContext, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err

View File

@ -10,7 +10,7 @@ func sortKeysOperator(d *dataTreeNavigator, context Context, expressionNode *Exp
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
rhs, err := d.GetMatchingNodes(context.SingleChildContext(candidate), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(candidate), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -13,6 +13,14 @@ var sortKeysOperatorScenarios = []expressionScenario{
"D0, P[], (doc)::{a: blah, b: bing, c: frog}\n", "D0, P[], (doc)::{a: blah, b: bing, c: frog}\n",
}, },
}, },
{
skipDoc: true,
document: `{c: frog}`,
expression: `sortKeys(.d)`,
expected: []string{
"D0, P[], (doc)::{c: frog}\n",
},
},
{ {
description: "Sort keys recursively", description: "Sort keys recursively",
subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted", subdescription: "Note the array elements are left unsorted, but maps inside arrays are sorted",

View File

@ -13,7 +13,9 @@ func getSubstituteParameters(d *dataTreeNavigator, block *ExpressionNode, contex
regEx := "" regEx := ""
replacementText := "" replacementText := ""
regExNodes, err := d.GetMatchingNodes(context, block.Lhs) readonlyContext := context.Clone()
readonlyContext.DontAutoCreate = true
regExNodes, err := d.GetMatchingNodes(readonlyContext, block.Lhs)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -78,7 +80,9 @@ func joinStringOperator(d *dataTreeNavigator, context Context, expressionNode *E
log.Debugf("-- joinStringOperator") log.Debugf("-- joinStringOperator")
joinStr := "" joinStr := ""
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) readonlyContext := context.Clone()
readonlyContext.DontAutoCreate = true
rhs, err := d.GetMatchingNodes(readonlyContext, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
@ -119,7 +123,9 @@ func splitStringOperator(d *dataTreeNavigator, context Context, expressionNode *
log.Debugf("-- splitStringOperator") log.Debugf("-- splitStringOperator")
splitStr := "" splitStr := ""
rhs, err := d.GetMatchingNodes(context, expressionNode.Rhs) readonlyContext := context.Clone()
readonlyContext.DontAutoCreate = true
rhs, err := d.GetMatchingNodes(readonlyContext, expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }

View File

@ -31,15 +31,20 @@ func uniqueBy(d *dataTreeNavigator, context Context, expressionNode *ExpressionN
var newMatches = orderedmap.NewOrderedMap() var newMatches = orderedmap.NewOrderedMap()
for _, node := range candidateNode.Content { for _, node := range candidateNode.Content {
child := &CandidateNode{Node: node} child := &CandidateNode{Node: node}
rhs, err := d.GetMatchingNodes(context.SingleChildContext(child), expressionNode.Rhs) rhs, err := d.GetMatchingNodes(context.SingleReadonlyChildContext(child), expressionNode.Rhs)
if err != nil { if err != nil {
return Context{}, err return Context{}, err
} }
keyValue := "null"
if rhs.MatchingNodes.Len() > 0 {
first := rhs.MatchingNodes.Front() first := rhs.MatchingNodes.Front()
keyCandidate := first.Value.(*CandidateNode) keyCandidate := first.Value.(*CandidateNode)
keyValue := keyCandidate.Node.Value keyValue = keyCandidate.Node.Value
}
_, exists := newMatches.Get(keyValue) _, exists := newMatches.Get(keyValue)
if !exists { if !exists {

View File

@ -39,6 +39,22 @@ var uniqueOperatorScenarios = []expressionScenario{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {name: billy, pet: dog}\n", "D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {name: billy, pet: dog}\n",
}, },
}, },
{
skipDoc: true,
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
expression: `unique_by(.name)`,
expected: []string{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n- {pet: fish}\n",
},
},
{
skipDoc: true,
document: `[{name: harry, pet: cat}, {pet: fish}, {name: harry, pet: dog}]`,
expression: `unique_by(.cat.dog)`,
expected: []string{
"D0, P[], (!!seq)::- {name: harry, pet: cat}\n",
},
},
} }
func TestUniqueOperatorScenarios(t *testing.T) { func TestUniqueOperatorScenarios(t *testing.T) {