Skip to content

Commit 730ab15

Browse files
committed
jsonpath: add support for non-integer array indices
This commit adds support for non-integer array indices, which adds Postgres compatibility in the JSONPath feature set. Non-integer array indices are rounded towards zero before being used as array indices (floor for positive numbers, ceil for negative numbers). Release note (sql change): Add support for non-integer array indices in JSONPath queries (ex. `SELECT jsonb_path_query('[1, 2, 3]', '$[2.5]');`). Indices are rounded towards 0.
1 parent 30a5b04 commit 730ab15

File tree

3 files changed

+105
-27
lines changed

3 files changed

+105
-27
lines changed

pkg/sql/logictest/testdata/logic_test/jsonb_path_query

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,22 +264,25 @@ SELECT jsonb_path_query('[1, 2, 3, 4, 5]', '$[1]');
264264
----
265265
2
266266

267-
# TODO(normanchenn): this will be changed after floats are supported.
268-
statement error pq: jsonpath array subscript is not a single numeric value
269-
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[0.99]')
267+
query T
268+
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[0.99]');
269+
----
270+
10
270271

271-
# TODO(normanchenn): this will be changed after floats are supported.
272-
statement error pq: jsonpath array subscript is not a single numeric value
273-
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[1.01]')
272+
query T
273+
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[1.01]');
274+
----
275+
9
274276

275277
query T
276278
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[$varInt]', '{"varInt": 1, "varFloat": 2.3, "varString": "a", "varBool": true, "varNull": null}')
277279
----
278280
9
279281

280-
# TODO(normanchenn): this will be changed after floats are supported.
281-
statement error pgcode 22033 jsonpath array subscript is not a single numeric value
282-
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[$varFloat]', '{"varInt": 1, "varFloat": 2.3, "varString": "a", "varBool": true, "varNull": null}')
282+
query T
283+
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[$varFloat]', '{"varInt": 1, "varFloat": 2.3, "varString": "a", "varBool": true, "varNull": null}');
284+
----
285+
8
283286

284287
statement error pgcode 22033 jsonpath array subscript is not a single numeric value
285288
SELECT jsonb_path_query('{"a": [10, 9, 8, 7]}', '$.a[$varString]', '{"varInt": 1, "varFloat": 2.3, "varString": "a", "varBool": true, "varNull": null}')
@@ -1552,3 +1555,48 @@ query T
15521555
SELECT jsonb_path_query('[1,2,3]', '$[last ? (@.type() == "number")]');
15531556
----
15541557
3
1558+
1559+
query T
1560+
SELECT jsonb_path_query('[1, 2, 3]', '$[1.1]');
1561+
----
1562+
2
1563+
1564+
query T
1565+
SELECT jsonb_path_query('[1, 2, 3]', '$[1.5]');
1566+
----
1567+
2
1568+
1569+
query T
1570+
SELECT jsonb_path_query('[1, 2, 3]', '$[1.99999999]');
1571+
----
1572+
2
1573+
1574+
query T
1575+
SELECT jsonb_path_query('[1, 2, 3]', '$[2.1]');
1576+
----
1577+
3
1578+
1579+
query T
1580+
SELECT jsonb_path_query('[1, 2, 3]', '$[2.5]');
1581+
----
1582+
3
1583+
1584+
query T
1585+
SELECT jsonb_path_query('[1, 2, 3]', '$[2.99999999]');
1586+
----
1587+
3
1588+
1589+
query T
1590+
SELECT jsonb_path_query('[1, 2, 3]', '$[-0.1]');
1591+
----
1592+
1
1593+
1594+
query T
1595+
SELECT jsonb_path_query('[1, 2, 3]', '$[-0.5]');
1596+
----
1597+
1
1598+
1599+
query T
1600+
SELECT jsonb_path_query('[1, 2, 3]', '$[-0.99999999]');
1601+
----
1602+
1

pkg/util/jsonpath/eval/array.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ package eval
88
import (
99
"math"
1010

11+
"github.com/cockroachdb/apd/v3"
1112
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
1213
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
14+
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
1315
"github.com/cockroachdb/cockroach/pkg/util/json"
1416
"github.com/cockroachdb/cockroach/pkg/util/jsonpath"
1517
"github.com/cockroachdb/errors"
@@ -125,11 +127,33 @@ func asInt(j json.JSON) (int, error) {
125127
if !ok {
126128
return 0, errIndexNotSingleNumValue
127129
}
128-
i64, err := d.Int64()
130+
131+
// First, try direct conversion to int64.
132+
if i64, err := d.Int64(); err == nil {
133+
return validateInt32Range(i64)
134+
}
135+
136+
// If direct conversion fails, truncate the non-integer towards zero.
137+
var err error
138+
dec := &apd.Decimal{}
139+
if d.Sign() == 1 {
140+
_, err = tree.ExactCtx.Floor(dec, d)
141+
} else {
142+
_, err = tree.ExactCtx.Ceil(dec, d)
143+
}
144+
if err != nil {
145+
return 0, errIndexNotSingleNumValue
146+
}
147+
148+
i64, err := dec.Int64()
129149
if err != nil {
130150
return 0, errIndexNotSingleNumValue
131151
}
132-
// Postgres returns an error if the index is outside int32 range.
152+
return validateInt32Range(i64)
153+
}
154+
155+
// validateInt32Range checks if the int64 value is within int32 range.
156+
func validateInt32Range(i64 int64) (int, error) {
133157
if i64 < math.MinInt32 || i64 > math.MaxInt32 {
134158
return 0, errInvalidSubscript
135159
}

pkg/util/jsonpath/parser/testdata/jsonpath

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -599,38 +599,44 @@ $.a.type()
599599
----
600600
$."a".type() -- normalized!
601601

602-
# postgres allows floats as array indexes
603-
# parse
604-
# $.abc[1.0]
605-
# ----
602+
# Postgres outputs $."abc"[1.0]. This might be a limitation of the apd library.
603+
parse
604+
$.abc[1.0]
605+
----
606+
$."abc"[1] -- normalized!
606607

607-
# parse
608-
# $.abc[1.1]
609-
# ----
608+
parse
609+
$.abc[1.1]
610+
----
611+
$."abc"[1.1] -- normalized!
610612

611613
# Postgres outputs $."abc"[1000000000].
612614
parse
613615
$.abc[1e9]
614616
----
615617
$."abc"[1E+9] -- normalized!
616618

617-
# parse
618-
# $.abc[0.0]
619-
# ----
619+
# Postgres outputs $."abc"[0.0].
620+
parse
621+
$.abc[0.0]
622+
----
623+
$."abc"[0] -- normalized!
620624

621625
# TODO(normanchenn): Related to the TODOs in pkg/util/jsonpath/operation.go.
622626
parse
623627
$.abc[-0]
624628
----
625629
$."abc"[(-0)] -- normalized!
626630

627-
# parse
628-
# $.abc[-1.99]
629-
# ----
631+
parse
632+
$.abc[-1.99]
633+
----
634+
$."abc"[(-1.99)] -- normalized!
630635

631-
# parse
632-
# $[1.999999999999999]
633-
# ----
636+
parse
637+
$[1.999999999999999]
638+
----
639+
$[1.999999999999999]
634640

635641
parse
636642
$[null]

0 commit comments

Comments
 (0)