Skip to content

Commit 743d8bd

Browse files
committedMar 19, 2025·
jsonpath/eval: add unwrap parameter to jsonpath.eval
Previously, jsonpath conditionals/filters would unwrap infinitely. For example, `SELECT jsonb_path_query('{"a": [[[[{"b": 1}]]]]}', '$.a ? (@.b == 1)');` would return an entry, when postgres would not. This commit adds an unwrap boolean parameter to `eval`, which allows for control over when to stop unwrapping json arrays. Additionally, this commit updates `eval` to take in a `json.JSON` rather than a `[]json.JSON`, simplifying the evaluation logic. Epic: None Release note: None
1 parent 1157d89 commit 743d8bd

File tree

7 files changed

+207
-177
lines changed

7 files changed

+207
-177
lines changed
 

‎pkg/sql/logictest/testdata/logic_test/jsonb_path_query

+9
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,15 @@ SELECT jsonb_path_query('{"a": [1,2,3]}', '$.a ? (@ > 10)');
692692
query empty
693693
SELECT jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.a ? (@.c > 100)');
694694

695+
query empty
696+
SELECT jsonb_path_query('{"a": [[[{"b": 1}], [{"b": 2}]], [[{"b": 2}], [{"b": 1}]]]}', '$.a ? (@.b == 1)');
697+
698+
query empty
699+
SELECT jsonb_path_query('{"a": [[[[[[{"b": 1}]]]]]]}', '$.a ? (@.b == 1)');
700+
701+
query empty
702+
SELECT jsonb_path_query('{"a": [[[{"b": 1}], [{"b": 2}]]]}', '$.a ? (@.b == 1)');
703+
695704
# when string literals are supported
696705
# query T rowsort
697706
# SELECT jsonb_path_query('{"data": [{"val": "a", "num": 1}, {"val": "b", "num": 2}, {"val": "a", "num": 3}]}'::jsonb, '$.data ? (@.val == "a")'::jsonpath);

‎pkg/util/jsonpath/eval/array.go

+44-64
Original file line numberDiff line numberDiff line change
@@ -13,97 +13,77 @@ import (
1313
"github.com/cockroachdb/errors"
1414
)
1515

16-
func (ctx *jsonpathCtx) evalArrayWildcard(current []json.JSON) ([]json.JSON, error) {
17-
var agg []json.JSON
18-
for _, j := range current {
19-
if j.Type() == json.ArrayJSONType {
20-
paths, err := json.AllPathsWithDepth(j, 1)
21-
if err != nil {
22-
return nil, err
23-
}
24-
for _, path := range paths {
25-
if path.Len() != 1 {
26-
return nil, errors.AssertionFailedf("unexpected path length")
27-
}
28-
unwrapped, err := path.FetchValIdx(0)
29-
if err != nil {
30-
return nil, err
31-
}
32-
if unwrapped == nil {
33-
return nil, errors.AssertionFailedf("unwrapping json element")
34-
}
35-
agg = append(agg, unwrapped)
36-
}
37-
} else if !ctx.strict {
38-
agg = append(agg, j)
39-
} else {
40-
return nil, pgerror.Newf(pgcode.SQLJSONArrayNotFound, "jsonpath wildcard array accessor can only be applied to an array")
41-
}
16+
func (ctx *jsonpathCtx) evalArrayWildcard(jsonValue json.JSON) ([]json.JSON, error) {
17+
if jsonValue.Type() == json.ArrayJSONType {
18+
// Do not evaluate any paths, just unwrap the current target.
19+
return ctx.unwrapCurrentTargetAndEval(nil /* jsonPath */, jsonValue, !ctx.strict /* unwrapNext */)
20+
} else if !ctx.strict {
21+
return []json.JSON{jsonValue}, nil
22+
} else {
23+
return nil, pgerror.Newf(pgcode.SQLJSONArrayNotFound, "jsonpath wildcard array accessor can only be applied to an array")
4224
}
43-
return agg, nil
4425
}
4526

4627
func (ctx *jsonpathCtx) evalArrayList(
47-
a jsonpath.ArrayList, current []json.JSON,
28+
arrayList jsonpath.ArrayList, jsonValue json.JSON,
4829
) ([]json.JSON, error) {
30+
if ctx.strict && jsonValue.Type() != json.ArrayJSONType {
31+
return nil, pgerror.Newf(pgcode.SQLJSONArrayNotFound, "jsonpath array accessor can only be applied to an array")
32+
}
4933
var agg []json.JSON
50-
for _, path := range a {
34+
for _, idxAccessor := range arrayList {
5135
var from, to int
5236
var err error
53-
if idxRange, ok := path.(jsonpath.ArrayIndexRange); ok {
54-
from, err = ctx.resolveArrayIndex(idxRange.Start, current)
37+
if idxRange, ok := idxAccessor.(jsonpath.ArrayIndexRange); ok {
38+
from, err = ctx.resolveArrayIndex(idxRange.Start, jsonValue)
5539
if err != nil {
5640
return nil, err
5741
}
58-
to, err = ctx.resolveArrayIndex(idxRange.End, current)
42+
to, err = ctx.resolveArrayIndex(idxRange.End, jsonValue)
5943
if err != nil {
6044
return nil, err
6145
}
6246
} else {
63-
from, err = ctx.resolveArrayIndex(path, current)
47+
from, err = ctx.resolveArrayIndex(idxAccessor, jsonValue)
6448
if err != nil {
6549
return nil, err
6650
}
6751
to = from
6852
}
6953

70-
for _, j := range current {
71-
if ctx.strict && j.Type() != json.ArrayJSONType {
72-
return nil, pgerror.Newf(pgcode.SQLJSONArrayNotFound,
73-
"jsonpath array accessor can only be applied to an array")
74-
}
75-
length := j.Len()
76-
if j.Type() != json.ArrayJSONType {
77-
length = 1
78-
}
79-
if ctx.strict && (from < 0 || from > to || to >= length) {
80-
return nil, pgerror.Newf(pgcode.InvalidSQLJSONSubscript,
81-
"jsonpath array subscript is out of bounds")
54+
length := jsonValue.Len()
55+
if jsonValue.Type() != json.ArrayJSONType {
56+
length = 1
57+
}
58+
if ctx.strict && (from < 0 || from > to || to >= length) {
59+
return nil, pgerror.Newf(pgcode.InvalidSQLJSONSubscript,
60+
"jsonpath array subscript is out of bounds")
61+
}
62+
for i := max(from, 0); i <= min(to, length-1); i++ {
63+
v, err := jsonArrayValueAtIndex(ctx, jsonValue, i)
64+
if err != nil {
65+
return nil, err
8266
}
83-
for i := max(from, 0); i <= min(to, length-1); i++ {
84-
v, err := jsonArrayValueAtIndex(ctx, j, i)
85-
if err != nil {
86-
return nil, err
87-
}
88-
if v == nil {
89-
continue
90-
}
91-
agg = append(agg, v)
67+
if v == nil {
68+
continue
9269
}
70+
agg = append(agg, v)
9371
}
9472
}
9573
return agg, nil
9674
}
9775

98-
func (ctx *jsonpathCtx) resolveArrayIndex(p jsonpath.Path, current []json.JSON) (int, error) {
99-
results, err := ctx.eval(p, current)
76+
func (ctx *jsonpathCtx) resolveArrayIndex(
77+
jsonPath jsonpath.Path, jsonValue json.JSON,
78+
) (int, error) {
79+
evalResults, err := ctx.eval(jsonPath, jsonValue, !ctx.strict /* unwrap */)
10080
if err != nil {
10181
return 0, err
10282
}
103-
if len(results) != 1 || results[0].Type() != json.NumberJSONType {
83+
if len(evalResults) != 1 || evalResults[0].Type() != json.NumberJSONType {
10484
return -1, pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is not a single numeric value")
10585
}
106-
i, err := asInt(results[0])
86+
i, err := asInt(evalResults[0])
10787
if err != nil {
10888
return -1, pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is not a single numeric value")
10989
}
@@ -122,22 +102,22 @@ func asInt(j json.JSON) (int, error) {
122102
return int(i64), nil
123103
}
124104

125-
func jsonArrayValueAtIndex(ctx *jsonpathCtx, j json.JSON, index int) (json.JSON, error) {
126-
if ctx.strict && j.Type() != json.ArrayJSONType {
105+
func jsonArrayValueAtIndex(ctx *jsonpathCtx, jsonValue json.JSON, index int) (json.JSON, error) {
106+
if ctx.strict && jsonValue.Type() != json.ArrayJSONType {
127107
return nil, pgerror.Newf(pgcode.SQLJSONArrayNotFound, "jsonpath array accessor can only be applied to an array")
128-
} else if j.Type() != json.ArrayJSONType {
108+
} else if jsonValue.Type() != json.ArrayJSONType {
129109
if index == 0 {
130-
return j, nil
110+
return jsonValue, nil
131111
}
132112
return nil, nil
133113
}
134114

135-
if ctx.strict && index >= j.Len() {
115+
if ctx.strict && index >= jsonValue.Len() {
136116
return nil, pgerror.Newf(pgcode.InvalidSQLJSONSubscript, "jsonpath array subscript is out of bounds")
137117
}
138118
if index < 0 {
139119
// Shouldn't happen, not supported in parser.
140120
return nil, errors.AssertionFailedf("negative array index")
141121
}
142-
return j.FetchValIdx(index)
122+
return jsonValue.FetchValIdx(index)
143123
}

‎pkg/util/jsonpath/eval/eval.go

+97-34
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,12 @@ func JsonpathQuery(
3939
vars: vars.JSON,
4040
strict: expr.Strict,
4141
}
42-
4342
// When silent is true, overwrite the strict mode.
4443
if bool(silent) {
4544
ctx.strict = false
4645
}
4746

48-
j, err := ctx.eval(expr.Path, []json.JSON{ctx.root})
47+
j, err := ctx.eval(expr.Path, ctx.root, !ctx.strict /* unwrap */)
4948
if err != nil {
5049
return nil, err
5150
}
@@ -56,68 +55,132 @@ func JsonpathQuery(
5655
return res, nil
5756
}
5857

59-
func (ctx *jsonpathCtx) eval(jp jsonpath.Path, current []json.JSON) ([]json.JSON, error) {
60-
switch p := jp.(type) {
58+
func (ctx *jsonpathCtx) eval(
59+
jsonPath jsonpath.Path, jsonValue json.JSON, unwrap bool,
60+
) ([]json.JSON, error) {
61+
switch path := jsonPath.(type) {
6162
case jsonpath.Paths:
62-
// Evaluate each path within the path list, update the current JSON
63-
// object after each evaluation.
64-
for _, path := range p {
65-
results, err := ctx.eval(path, current)
63+
results := []json.JSON{jsonValue}
64+
var err error
65+
for _, p := range path {
66+
results, err = ctx.evalArray(p, results, unwrap)
6667
if err != nil {
6768
return nil, err
6869
}
69-
current = results
7070
}
71-
return current, nil
71+
return results, nil
7272
case jsonpath.Root:
7373
return []json.JSON{ctx.root}, nil
7474
case jsonpath.Current:
75-
return current, nil
75+
return []json.JSON{jsonValue}, nil
7676
case jsonpath.Key:
77-
return ctx.evalKey(p, current)
77+
return ctx.evalKey(path, jsonValue, unwrap)
7878
case jsonpath.Wildcard:
79-
return ctx.evalArrayWildcard(current)
79+
return ctx.evalArrayWildcard(jsonValue)
8080
case jsonpath.ArrayList:
81-
return ctx.evalArrayList(p, current)
81+
return ctx.evalArrayList(path, jsonValue)
8282
case jsonpath.Scalar:
83-
resolved, err := ctx.resolveScalar(p)
83+
resolved, err := ctx.resolveScalar(path)
8484
if err != nil {
8585
return nil, err
8686
}
8787
return []json.JSON{resolved}, nil
8888
case jsonpath.Operation:
89-
return ctx.evalOperation(p, current)
89+
res, err := ctx.evalOperation(path, jsonValue)
90+
if err != nil {
91+
return nil, err
92+
}
93+
return convertFromBool(res), nil
9094
case jsonpath.Filter:
91-
return ctx.evalFilter(p, current)
95+
return ctx.evalFilter(path, jsonValue, unwrap)
9296
default:
9397
return nil, errUnimplemented
9498
}
9599
}
96100

97-
func (ctx *jsonpathCtx) unwrap(input json.JSON) []json.JSON {
98-
if !ctx.strict && input.Type() == json.ArrayJSONType {
99-
array, _ := input.AsArray()
100-
return array
101+
func (ctx *jsonpathCtx) evalArray(
102+
jsonPath jsonpath.Path, jsonValue []json.JSON, unwrap bool,
103+
) ([]json.JSON, error) {
104+
var agg []json.JSON
105+
for _, j := range jsonValue {
106+
arr, err := ctx.eval(jsonPath, j, unwrap)
107+
if err != nil {
108+
return nil, err
109+
}
110+
agg = append(agg, arr...)
111+
}
112+
return agg, nil
113+
}
114+
115+
// unwrapCurrentTargetAndEval is used to unwrap the current json array and evaluate
116+
// the jsonpath query on each element. It is similar to executeItemUnwrapTargetArray
117+
// in postgres/src/backend/utils/adt/jsonpath_exec.c.
118+
func (ctx *jsonpathCtx) unwrapCurrentTargetAndEval(
119+
jsonPath jsonpath.Path, jsonValue json.JSON, unwrapNext bool,
120+
) ([]json.JSON, error) {
121+
if jsonValue.Type() != json.ArrayJSONType {
122+
return nil, errors.AssertionFailedf("unwrapCurrentTargetAndEval can only be applied to an array")
101123
}
102-
return []json.JSON{input}
124+
return ctx.executeAnyItem(jsonPath, jsonValue, unwrapNext)
103125
}
104126

105-
func (ctx *jsonpathCtx) evalAndUnwrap(path jsonpath.Path, inputs []json.JSON) ([]json.JSON, error) {
106-
results, err := ctx.eval(path, inputs)
127+
func (ctx *jsonpathCtx) executeAnyItem(
128+
jsonPath jsonpath.Path, jsonValue json.JSON, unwrapNext bool,
129+
) ([]json.JSON, error) {
130+
childItems, err := json.AllPathsWithDepth(jsonValue, 1 /* depth */)
107131
if err != nil {
108132
return nil, err
109133
}
110-
if ctx.strict {
111-
return results, nil
112-
}
113-
var unwrapped []json.JSON
114-
for _, result := range results {
115-
unwrapped = append(unwrapped, ctx.unwrap(result)...)
134+
var agg []json.JSON
135+
for _, item := range childItems {
136+
if item.Len() != 1 {
137+
return nil, errors.AssertionFailedf("unexpected path length")
138+
}
139+
unwrappedItem, err := item.FetchValIdx(0 /* idx */)
140+
if err != nil {
141+
return nil, err
142+
}
143+
if unwrappedItem == nil {
144+
return nil, errors.AssertionFailedf("unwrapping json element")
145+
}
146+
if jsonPath == nil {
147+
agg = append(agg, unwrappedItem)
148+
} else {
149+
evalResults, err := ctx.eval(jsonPath, unwrappedItem, unwrapNext)
150+
if err != nil {
151+
return nil, err
152+
}
153+
agg = append(agg, evalResults...)
154+
}
116155
}
117-
return unwrapped, nil
156+
return agg, nil
118157
}
119158

120-
func (ctx *jsonpathCtx) unwrapAndEval(path jsonpath.Path, input json.JSON) ([]json.JSON, error) {
121-
unwrapped := ctx.unwrap(input)
122-
return ctx.eval(path, unwrapped)
159+
// evalAndUnwrapResult is used to evaluate the jsonpath query and unwrap the result
160+
// if the unwrap flag is true. It is similar to executeItemOptUnwrapResult
161+
// in postgres/src/backend/utils/adt/jsonpath_exec.c.
162+
func (ctx *jsonpathCtx) evalAndUnwrapResult(
163+
jsonPath jsonpath.Path, jsonValue json.JSON, unwrap bool,
164+
) ([]json.JSON, error) {
165+
evalResults, err := ctx.eval(jsonPath, jsonValue, !ctx.strict /* unwrap */)
166+
if err != nil {
167+
return nil, err
168+
}
169+
if unwrap && !ctx.strict {
170+
var agg []json.JSON
171+
for _, j := range evalResults {
172+
if j.Type() == json.ArrayJSONType {
173+
// Pass in nil to just unwrap the array.
174+
arr, err := ctx.unwrapCurrentTargetAndEval(nil /* jsonPath */, j, false /* unwrapNext */)
175+
if err != nil {
176+
return nil, err
177+
}
178+
agg = append(agg, arr...)
179+
} else {
180+
agg = append(agg, j)
181+
}
182+
}
183+
return agg, nil
184+
}
185+
return evalResults, nil
123186
}

‎pkg/util/jsonpath/eval/filter.go

+15-25
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,23 @@ package eval
88
import (
99
"github.com/cockroachdb/cockroach/pkg/util/json"
1010
"github.com/cockroachdb/cockroach/pkg/util/jsonpath"
11-
"github.com/cockroachdb/errors"
1211
)
1312

14-
func (ctx *jsonpathCtx) evalFilter(p jsonpath.Filter, current []json.JSON) ([]json.JSON, error) {
15-
// TODO(normanchenn): clean up.
16-
var unwrapped []json.JSON
17-
for _, j := range current {
18-
unwrapped = append(unwrapped, ctx.unwrap(j)...)
13+
func (ctx *jsonpathCtx) evalFilter(
14+
p jsonpath.Filter, current json.JSON, unwrap bool,
15+
) ([]json.JSON, error) {
16+
if unwrap && current.Type() == json.ArrayJSONType {
17+
return ctx.unwrapCurrentTargetAndEval(p, current, false /* unwrapNext */)
1918
}
20-
current = unwrapped
21-
var filtered []json.JSON
22-
// unwrap before
23-
for _, j := range current {
24-
results, err := ctx.eval(p.Condition, []json.JSON{j})
25-
if err != nil {
26-
// Postgres doesn't error when there's a structural error within
27-
// filter condition, and will return nothing instead.
28-
return []json.JSON{}, nil //nolint:returnerrcheck
29-
}
30-
if len(results) != 1 || !isBool(results[0]) {
31-
return nil, errors.New("filter condition must evaluate to a boolean")
32-
}
33-
34-
condition, _ := results[0].AsBool()
35-
if condition {
36-
filtered = append(filtered, j)
37-
}
19+
results, err := ctx.eval(p.Condition, current, !ctx.strict /* unwrap */)
20+
if err != nil || len(results) != 1 || !isBool(results[0]) {
21+
// Postgres doesn't error when there's a structure error within filter
22+
// conditions, and will return nothing instead.
23+
return []json.JSON{}, nil //nolint:returnerrcheck
24+
}
25+
condition, _ := results[0].AsBool()
26+
if condition {
27+
return []json.JSON{current}, nil
3828
}
39-
return filtered, nil
29+
return []json.JSON{}, nil
4030
}

‎pkg/util/jsonpath/eval/key.go

+18-31
Original file line numberDiff line numberDiff line change
@@ -10,39 +10,26 @@ import (
1010
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
1111
"github.com/cockroachdb/cockroach/pkg/util/json"
1212
"github.com/cockroachdb/cockroach/pkg/util/jsonpath"
13-
"github.com/cockroachdb/errors"
1413
)
1514

16-
func (ctx *jsonpathCtx) evalKey(k jsonpath.Key, current []json.JSON) ([]json.JSON, error) {
17-
var agg []json.JSON
18-
for _, j := range current {
19-
if j.Type() == json.ObjectJSONType {
20-
val, err := j.FetchValKey(string(k))
21-
if err != nil {
22-
return nil, err
23-
}
24-
if val == nil {
25-
if ctx.strict {
26-
return nil, pgerror.Newf(pgcode.SQLJSONMemberNotFound, "JSON object does not contain key %q", string(k))
27-
}
28-
continue
29-
}
30-
agg = append(agg, val)
31-
} else if !ctx.strict && j.Type() == json.ArrayJSONType {
32-
arr, ok := j.AsArray()
33-
if !ok {
34-
return nil, errors.AssertionFailedf("array expected")
35-
}
36-
for _, elem := range arr {
37-
results, err := ctx.eval(k, []json.JSON{elem})
38-
if err != nil {
39-
return nil, err
40-
}
41-
agg = append(agg, results...)
42-
}
43-
} else if ctx.strict {
44-
return nil, pgerror.Newf(pgcode.SQLJSONMemberNotFound, "jsonpath member accessor can only be applied to an object")
15+
func (ctx *jsonpathCtx) evalKey(
16+
key jsonpath.Key, jsonValue json.JSON, unwrap bool,
17+
) ([]json.JSON, error) {
18+
if jsonValue.Type() == json.ObjectJSONType {
19+
val, err := jsonValue.FetchValKey(string(key))
20+
if err != nil {
21+
return nil, err
4522
}
23+
if val == nil && ctx.strict {
24+
return nil, pgerror.Newf(pgcode.SQLJSONMemberNotFound, "JSON object does not contain key %q", string(key))
25+
} else if val != nil {
26+
return []json.JSON{val}, nil
27+
}
28+
return []json.JSON{}, nil
29+
} else if unwrap && jsonValue.Type() == json.ArrayJSONType {
30+
return ctx.unwrapCurrentTargetAndEval(key, jsonValue, false /* unwrapNext */)
31+
} else if ctx.strict {
32+
return nil, pgerror.Newf(pgcode.SQLJSONMemberNotFound, "jsonpath member accessor can only be applied to an object")
4633
}
47-
return agg, nil
34+
return []json.JSON{}, nil
4835
}

‎pkg/util/jsonpath/eval/operation.go

+19-18
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,35 @@ func convertToBool(j json.JSON) jsonpathBool {
5353
}
5454

5555
func (ctx *jsonpathCtx) evalOperation(
56-
op jsonpath.Operation, current []json.JSON,
57-
) ([]json.JSON, error) {
56+
op jsonpath.Operation, jsonValue json.JSON,
57+
) (jsonpathBool, error) {
5858
switch op.Type {
5959
case jsonpath.OpLogicalAnd, jsonpath.OpLogicalOr, jsonpath.OpLogicalNot:
60-
res, err := ctx.evalLogical(op, current)
60+
res, err := ctx.evalLogical(op, jsonValue)
6161
if err != nil {
62-
return nil, err
62+
return jsonpathBoolUnknown, err
6363
}
64-
return convertFromBool(res), nil
64+
return res, nil
6565
case jsonpath.OpCompEqual, jsonpath.OpCompNotEqual,
6666
jsonpath.OpCompLess, jsonpath.OpCompLessEqual,
6767
jsonpath.OpCompGreater, jsonpath.OpCompGreaterEqual:
68-
res, err := ctx.evalComparison(op, current)
68+
res, err := ctx.evalComparison(op, jsonValue, true /* unwrapRight */)
6969
if err != nil {
70-
return nil, err
70+
return jsonpathBoolUnknown, err
7171
}
72-
return convertFromBool(res), nil
72+
return res, nil
7373
default:
7474
panic(errors.AssertionFailedf("unhandled operation type"))
7575
}
7676
}
7777

7878
func (ctx *jsonpathCtx) evalLogical(
79-
op jsonpath.Operation, current []json.JSON,
79+
op jsonpath.Operation, current json.JSON,
8080
) (jsonpathBool, error) {
81-
left, err := ctx.eval(op.Left, current)
81+
left, err := ctx.eval(op.Left, current, !ctx.strict /* unwrap */)
8282
if err != nil {
8383
return jsonpathBoolUnknown, err
8484
}
85-
8685
if len(left) != 1 || !isBool(left[0]) {
8786
return jsonpathBoolUnknown, errors.AssertionFailedf("left is not a boolean")
8887
}
@@ -108,16 +107,14 @@ func (ctx *jsonpathCtx) evalLogical(
108107
panic(errors.AssertionFailedf("unhandled logical operation type"))
109108
}
110109

111-
right, err := ctx.eval(op.Right, current)
110+
right, err := ctx.eval(op.Right, current, !ctx.strict /* unwrap */)
112111
if err != nil {
113112
return jsonpathBoolUnknown, err
114113
}
115-
116114
if len(right) != 1 || !isBool(right[0]) {
117115
return jsonpathBoolUnknown, errors.AssertionFailedf("right is not a boolean")
118116
}
119117
rightBool := convertToBool(right[0])
120-
121118
switch op.Type {
122119
case jsonpath.OpLogicalAnd:
123120
if rightBool == jsonpathBoolTrue {
@@ -139,13 +136,17 @@ func (ctx *jsonpathCtx) evalLogical(
139136
// right paths satisfy the condition. In strict mode, even if a pair has been
140137
// found, all pairs need to be checked for errors.
141138
func (ctx *jsonpathCtx) evalComparison(
142-
p jsonpath.Operation, current []json.JSON,
139+
op jsonpath.Operation, jsonValue json.JSON, unwrapRight bool,
143140
) (jsonpathBool, error) {
144-
left, err := ctx.evalAndUnwrap(p.Left, current)
141+
// The left argument results are always auto-unwrapped.
142+
left, err := ctx.evalAndUnwrapResult(op.Left, jsonValue, true /* unwrap */)
145143
if err != nil {
146144
return jsonpathBoolUnknown, err
147145
}
148-
right, err := ctx.evalAndUnwrap(p.Right, current)
146+
// The right argument results are conditionally unwrapped. Currently, it is
147+
// always unwrapped, but in the future for operations like like_regex, we
148+
// don't want to unwrap the right argument.
149+
right, err := ctx.evalAndUnwrapResult(op.Right, jsonValue, unwrapRight)
149150
if err != nil {
150151
return jsonpathBoolUnknown, err
151152
}
@@ -154,7 +155,7 @@ func (ctx *jsonpathCtx) evalComparison(
154155
found := false
155156
for _, l := range left {
156157
for _, r := range right {
157-
res, err := execComparison(l, r, p.Type)
158+
res, err := execComparison(l, r, op.Type)
158159
if err != nil {
159160
return jsonpathBoolUnknown, err
160161
}

‎pkg/util/jsonpath/eval/scalar.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import (
1212
"github.com/cockroachdb/cockroach/pkg/util/jsonpath"
1313
)
1414

15-
func (ctx *jsonpathCtx) resolveScalar(s jsonpath.Scalar) (json.JSON, error) {
16-
if s.Type == jsonpath.ScalarVariable {
17-
val, err := ctx.vars.FetchValKey(s.Variable)
15+
func (ctx *jsonpathCtx) resolveScalar(scalar jsonpath.Scalar) (json.JSON, error) {
16+
if scalar.Type == jsonpath.ScalarVariable {
17+
val, err := ctx.vars.FetchValKey(scalar.Variable)
1818
if err != nil {
1919
return nil, err
2020
}
2121
if val == nil {
22-
return nil, pgerror.Newf(pgcode.UndefinedObject, "could not find jsonpath variable %q", s.Variable)
22+
return nil, pgerror.Newf(pgcode.UndefinedObject, "could not find jsonpath variable %q", scalar.Variable)
2323
}
2424
return val, nil
2525
}
26-
return s.Value, nil
26+
return scalar.Value, nil
2727
}

0 commit comments

Comments
 (0)
Please sign in to comment.