Files
Michiel van Slobbe b904884b94 Improve performance of string output (#3643)
Printing a string wrote each character with a separate call, one
fwrite() per character, or one jv_string_append_buf() for tojson and
tostring, which made output of string-heavy data slow.

Scan ahead for the next byte that needs escaping and write the run of
bytes before it with a single call, without changing the output.
Printing strings is now significantly faster, most of all for long
strings with few characters to escape, and the same applies to tojson
and tostring. With --ascii-output, text that is almost all non-ASCII is
about the same speed, since nearly every byte is escaped anyway.
2026-10-10 21:50:10 +09:00

2831 lines
57 KiB
Plaintext

# Tests are groups of three lines: program, input, expected output
# Blank lines and lines starting with # are ignored
#
# Simple value tests to check parser. Input is irrelevant
#
true
null
true
false
null
false
null
42
null
1
null
1
-1
null
-1
# FIXME: much more number testing needed
{}
null
{}
[]
null
[]
{x:-1},{x:-.},{x:-.|abs}
1
{"x":-1}
{"x":-1}
{"x":1}
# We test escapes by matching them against Unicode codepoints
# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs)
"Aa\r\n\t\b\f\u03bc"
null
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
.
"Aa\r\n\t\b\f\u03bc"
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
%%FAIL
"u\vw"
jq: error: Invalid escape at line 1, column 4 (while parsing '"\v"') at <top-level>, line 1, column 3:
"u\vw"
^^
"inter\("pol" + "ation")"
null
"interpolation"
@text,@json,([1,.]|@csv,@tsv),@html,(@uri|.,@urid),@sh,(@base64|.,@base64d)
"!()<>&'\"\t"
"!()<>&'\"\t"
"\"!()<>&'\\\"\\t\""
"1,\"!()<>&'\"\"\t\""
"1\t!()<>&'\"\\t"
"!()&lt;&gt;&amp;&apos;&quot;\t"
"%21%28%29%3C%3E%26%27%22%09"
"!()<>&'\"\t"
"'!()<>&'\\''\"\t'"
"ISgpPD4mJyIJ"
"!()<>&'\"\t"
# regression test for #436
@base64
"foóbar\n"
"Zm/Ds2Jhcgo="
@base64d
"Zm/Ds2Jhcgo="
"foóbar\n"
@uri
"\u03bc"
"%CE%BC"
@urid
"%CE%BC"
"\u03bc"
@html "<b>\(.)</b>"
"<script>hax</script>"
"<b>&lt;script&gt;hax&lt;/script&gt;</b>"
[.[]|tojson|fromjson]
["foo", 1, ["a", 1, "b", 2, {"foo":"bar"}]]
["foo",1,["a",1,"b",2,{"foo":"bar"}]]
# Put escapes on both sides of runs of bytes that need no escaping
map(tojson)
["abcdefg\"", "abcdefgh\\", "abcdefghi\u0001j", "abcdefghijklmno\u007f", "abcdefgh\té", "abcdefghijklmnop\n"]
["\"abcdefg\\\"\"","\"abcdefgh\\\\\"","\"abcdefghi\\u0001j\"","\"abcdefghijklmno\\u007f\"","\"abcdefgh\\té\"","\"abcdefghijklmnop\\n\""]
[range(18) as $i | ("\"", "\\", "\u0001", "\u007f", "é", "😀") as $c | "abcdefghijklmnopq"[:$i] + $c + "abcdefghijklmnopq"[$i:] | (tojson | fromjson) == .] | all
null
true
#
# Dictionary construction syntax
#
{a: 1}
null
{"a":1}
{a,b,(.d):.a,e:.b}
{"a":1, "b":2, "c":3, "d":"c"}
{"a":1, "b":2, "c":1, "e":2}
{"a",b,"a$\(1+1)"}
{"a":1, "b":2, "c":3, "a$2":4}
{"a":1, "b":2, "a$2":4}
%%FAIL
{(0):1}
jq: error: Cannot use number (0) as object key at <top-level>, line 1, column 3:
{(0):1}
^
%%FAIL
{1+2:3}
jq: error: May need parentheses around object key expression at <top-level>, line 1, column 2:
{1+2:3}
^^^
%%FAIL
{non_const:., (0):1}
jq: error: Cannot use number (0) as object key at <top-level>, line 1, column 16:
{non_const:., (0):1}
^
#
# Field access, piping
#
.foo
{"foo": 42, "bar": 43}
42
.foo | .bar
{"foo": {"bar": 42}, "bar": "badvalue"}
42
.foo.bar
{"foo": {"bar": 42}, "bar": "badvalue"}
42
.foo_bar
{"foo_bar": 2}
2
.["foo"].bar
{"foo": {"bar": 42}, "bar": "badvalue"}
42
."foo"."bar"
{"foo": {"bar": 20}}
20
.e0, .E1, .E-1, .E+1
{"e0": 1, "E1": 2, "E": 3}
1
2
2
4
[.[]|.foo?]
[1,[2],{"foo":3,"bar":4},{},{"foo":5}]
[3,null,5]
[.[]|.foo?.bar?]
[1,[2],[],{"foo":3},{"foo":{"bar":4}},{}]
[4,null]
[..]
[1,[[2]],{ "a":[1]}]
[[1,[[2]],{"a":[1]}],1,[[2]],[2],2,{"a":[1]},[1],1]
[.[]|.[]?]
[1,null,[],[1,[2,[[3]]]],[{}],[{"a":[1,[2]]}]]
[1,[2,[[3]]],{},{"a":[1,[2]]}]
[.[]|.[1:3]?]
[1,null,true,false,"abcdef",{},{"a":1,"b":2},[],[1,2,3,4,5],[1,2]]
[null,"bc",[],[2,3],[2]]
# chaining/suffix-list, with and without dot
map(try .a[] catch ., try .a.[] catch ., .a[]?, .a.[]?)
[{"a": [1,2]}, {"a": 123}]
[1,2,1,2,1,2,1,2,"Cannot iterate over number (123)","Cannot iterate over number (123)"]
# oss-fuzz #66070: objects[] leaks if a non-last element throws an error
try ["OK", (.[] | error)] catch ["KO", .]
{"a":["b"],"c":["d"]}
["KO",["b"]]
#
# Negative array indices
#
try (.foo[-1] = 0) catch .
null
"Out of bounds negative array index"
try (.foo[-2] = 0) catch .
null
"Out of bounds negative array index"
.[-1] = 5
[0,1,2]
[0,1,5]
.[-2] = 5
[0,1,2]
[0,5,2]
try (.[999999999] = 0) catch .
null
"Array index too large"
#
# Multiple outputs, iteration
#
.[]
[1,2,3]
1
2
3
1,1
[]
1
1
1,.
[]
1
[]
[.]
[2]
[[2]]
[[2]]
[3]
[[2]]
[{}]
[2]
[{}]
[.[]]
["a"]
["a"]
[(.,1),((.,.[]),(2,3))]
["a","b"]
[["a","b"],1,["a","b"],"a","b",2,3]
[([5,5][]),.,.[]]
[1,2,3]
[5,5,[1,2,3],1,2,3]
{x: (1,2)},{x:3} | .x
null
1
2
3
[.[-4,-3,-2,-1,0,1,2,3]]
[1,2,3]
[null,1,2,3,1,2,3,null]
[range(0;10)]
null
[0,1,2,3,4,5,6,7,8,9]
[range(0,1;3,4)]
null
[0,1,2, 0,1,2,3, 1,2, 1,2,3]
[range(0;10;3)]
null
[0,3,6,9]
[range(0;10;-1)]
null
[]
[range(0;-5;-1)]
null
[0,-1,-2,-3,-4]
[range(0;-10;-3)]
null
[0,-3,-6,-9]
[range(0;10;0)]
null
[]
[range(0,1;4,5;1,2)]
null
[0,1,2,3,0,2, 0,1,2,3,4,0,2,4, 1,2,3,1,3, 1,2,3,4,1,3]
try range(.) catch .
null
"Range bounds and step must be numeric"
try range(0;"") catch .
null
"Range bounds and step must be numeric"
try range(0;1;[]) catch .
null
"Range bounds and step must be numeric"
[while(.<100; .*2)]
1
[1,2,4,8,16,32,64]
[(label $here | .[] | if .>1 then break $here else . end), "hi!"]
[0,1,2]
[0,1,"hi!"]
[(label $here | .[] | if .>1 then break $here else . end), "hi!"]
[0,2,1]
[0,"hi!"]
%%FAIL
. as $foo | break $foo
jq: error: $*label-foo is not defined at <top-level>, line 1, column 13:
. as $foo | break $foo
^^^^^^^^^^
[.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]]
[1,2,3,4,5]
[1,2,6,24,120]
[label $out | foreach .[] as $item ([3, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1])]
[11,22,33,44,55,66,77,88,99]
[11,22,33]
[foreach range(5) as $item (0; $item)]
null
[0,1,2,3,4]
[foreach .[] as [$i, $j] (0; . + $i - $j)]
[[2,1], [5,3], [6,4]]
[1,3,5]
[foreach .[] as {a:$a} (0; . + $a; -.)]
[{"a":1}, {"b":2}, {"a":3, "b":4}]
[-1, -1, -4]
[-foreach -.[] as $x (0; . + $x)]
[1,2,3]
[1,3,6]
[foreach .[] / .[] as $i (0; . + $i)]
[1,2]
[1,3,3.5,4.5]
[foreach .[] as $x (0; . + $x) as $x | $x]
[1,2,3]
[1,3,6]
[limit(3; .[])]
[11,22,33,44,55,66,77,88,99]
[11,22,33]
[limit(0; error)]
"badness"
[]
[limit(1; 1, error)]
"badness"
[1]
try limit(-1; error) catch .
null
"limit doesn't support negative count"
[skip(3; .[])]
[1,2,3,4,5,6,7,8,9]
[4,5,6,7,8,9]
[skip(0,2,3,4; .[])]
[1,2,3]
[1,2,3,3]
[skip(3; .[])]
[]
[]
try skip(-1; error) catch .
null
"skip doesn't support negative count"
nth(1; 0,1,error("foo"))
null
1
[first(range(.)), last(range(.))]
10
[0,9]
[first(range(.)), last(range(.))]
0
[]
[nth(0,5,9,10,15; range(.)), try nth(-1; range(.)) catch .]
10
[0,5,9,"nth doesn't support negative indices"]
# Check that first(g) does not extract more than one value from g
first(1,error("foo"))
null
1
#
# Check that various builtins evaluate all arguments where appropriate,
# doing cartesian products where appropriate.
#
# Check that limit does work for each value produced by n!
[limit(5,7; range(9))]
null
[0,1,2,3,4,0,1,2,3,4,5,6]
# Same check for nth
[nth(5,7; range(9;0;-1))]
null
[4,2]
# Same check for range/3
[range(0,1,2;4,3,2;2,3)]
null
[0,2,0,3,0,2,0,0,0,1,3,1,1,1,1,1,2,2,2,2]
# Same check for range/1
[range(3,5)]
null
[0,1,2,0,1,2,3,4]
# Same check for index/1, rindex/1, indices/1
[(index(",","|"), rindex(",","|")), indices(",","|")]
"a,b|c,d,e||f,g,h,|,|,i,j"
[1,3,22,19,[1,5,7,12,14,16,18,20,22],[3,9,10,17,19]]
# Same check for join/1
join(",","/")
["a","b","c","d"]
"a,b,c,d"
"a/b/c/d"
[.[]|join("a")]
[[],[""],["",""],["","",""]]
["","","a","aa"]
# Same check for flatten/1
flatten(3,2,1)
[0, [1], [[2]], [[[3]]]]
[0,1,2,3]
[0,1,2,[3]]
[0,1,[2],[[3]]]
#
# Slices
#
[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]]
[0,1,2,3,4,5,6]
[[], [2,3], [0,1,2,3,4], [5,6], [], []]
[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]]
"abcdefghi"
["","","abcdefg","hi","",""]
del(.[2:4],.[0],.[-2:])
[0,1,2,3,4,5,6,7]
[1,4,5]
.[2:4] = ([], ["a","b"], ["a","b","c"])
[0,1,2,3,4,5,6,7]
[0,1,4,5,6,7]
[0,1,"a","b",4,5,6,7]
[0,1,"a","b","c",4,5,6,7]
# Slices at large offsets (issue #1108)
#
# This is written this way because [range(<large number>)] is
# significantly slower under valgrind than .[<large number>] = value.
#
# We range down rather than up so that we have just one realloc.
reduce range(65540;65536;-1) as $i ([]; .[$i] = $i)|.[65536:]
null
[null,65537,65538,65539,65540]
#
# Variables
#
1 as $x | 2 as $y | [$x,$y,$x]
null
[1,2,1]
[1,2,3][] as $x | [[4,5,6,7][$x]]
null
[5]
[6]
[7]
42 as $x | . | . | . + 432 | $x + 1
34324
43
1 + 2 as $x | -$x
null
-3
"x" as $x | "a"+"y" as $y | $x+","+$y
null
"x,ay"
1 as $x | [$x,$x,$x as $x | $x]
null
[1,1,1]
[1, {c:3, d:4}] as [$a, {c:$b, b:$c}] | $a, $b, $c
null
1
3
null
. as {as: $kw, "str": $str, ("e"+"x"+"p"): $exp} | [$kw, $str, $exp]
{"as": 1, "str": 2, "exp": 3}
[1, 2, 3]
.[] as [$a, $b] | [$b, $a]
[[1], [1, 2, 3]]
[null, 1]
[2, 1]
. as $i | . as [$i] | $i
[0]
0
. as [$i] | . as $i | $i
[0]
[0]
%%FAIL
. as [] | null
jq: error: syntax error, unexpected ']', expecting BINDING or '[' or '{' at <top-level>, line 1, column 7:
. as [] | null
^
%%FAIL
. as {} | null
jq: error: syntax error, unexpected '}' at <top-level>, line 1, column 7:
. as {} | null
^
%%FAIL
. as $foo | [$foo, $bar]
jq: error: $bar is not defined at <top-level>, line 1, column 20:
. as $foo | [$foo, $bar]
^^^^
%%FAIL
. as {(true):$foo} | $foo
jq: error: Cannot use boolean (true) as object key at <top-level>, line 1, column 8:
. as {(true):$foo} | $foo
^^^^
# [.,(.[] | {x:.},.),.,.[]]
#
# Builtin functions
#
1+1
null
2
1+1
"wtasdf"
2.0
2-1
null
1
2-(-1)
null
3
1e+0+0.001e3
"I wonder what this will be?"
20e-1
.+4
15
19.0
.+null
{"a":42}
{"a":42}
null+.
null
null
.a+.b
{"a":42}
42
[1,2,3] + [.]
null
[1,2,3,null]
{"a":1} + {"b":2} + {"c":3}
"asdfasdf"
{"a":1, "b":2, "c":3}
"asdf" + "jkl;" + . + . + .
"some string"
"asdfjkl;some stringsome stringsome string"
"\u0000\u0020\u0000" + .
"\u0000\u0020\u0000"
"\u0000 \u0000\u0000 \u0000"
42 - .
11
31
[1,2,3,4,1] - [.,3]
1
[2,4]
[-1 as $x | 1,$x]
null
[1,-1]
[10 * 20, 20 / .]
4
[200, 5]
1 + 2 * 2 + 10 / 2
null
10
[16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2]
null
[2, 8, 10, 14]
1e-19 + 1e-20 - 5e-21
null
1.05e-19
1 / 1e-17
null
1e+17
9E999999999, 9999999999E999999990, 1E-999999999, 0.000000001E-999999990
null
9E+999999999
9.999999999E+999999999
1E-999999999
1E-999999999
5E500000000 > 5E-5000000000, 10000E500000000 > 10000E-5000000000
null
true
true
# #2825
(1e999999999, 10e999999999) > (1e-1147483646, 0.1e-1147483646)
null
true
true
true
true
25 % 7
null
4
49732 % 472
null
172
[(infinite, -infinite) % (1, -1, infinite)]
null
[0,0,0,0,0,-1]
[nan % 1, 1 % nan | isnan]
null
[true,true]
1 + tonumber + ("10" | tonumber)
4
15
"123\u0000456" | try tonumber catch .
null
"string (\"123\\u0000456\") cannot be parsed as a number"
map(toboolean)
["false","true",false,true]
[false,true,false,true]
.[] | try toboolean catch .
[null,0,"tru","truee","fals","falsee",[],{}]
"null (null) cannot be parsed as a boolean"
"number (0) cannot be parsed as a boolean"
"string (\"tru\") cannot be parsed as a boolean"
"string (\"truee\") cannot be parsed as a boolean"
"string (\"fals\") cannot be parsed as a boolean"
"string (\"falsee\") cannot be parsed as a boolean"
"array ([]) cannot be parsed as a boolean"
"object ({}) cannot be parsed as a boolean"
"true\u0000x", "false\u0000" | try toboolean catch .
null
"string (\"true\\u0000x\") cannot be parsed as a boolean"
"string (\"false\\u0000\") cannot be parsed as a boolean"
[{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]]
{"object": {"a":42}, "num":10.0}
[true, true, false, false, false, false, false, false, false]
[true, true, false, false, false, false, false, false, false]
[false, false, true, true, false, false, false, false, false]
[false, false, true, true, false, false, false, false, false]
[false, false, false, false, true, false, false, false, false]
[false, false, false, false, false, true, false, false, false]
[false, false, false, false, false, false, true, false, false]
[false, false, false, false, false, false, false, true, false]
[false, false, false, false, false, false, false, false, true ]
[.[] | length]
[[], {}, [1,2], {"a":42}, "asdf", "\u03bc"]
[0, 0, 2, 1, 4, 1]
utf8bytelength
"asdf\u03bc"
6
[.[] | try utf8bytelength catch .]
[[], {}, [1,2], 55, true, false]
["array ([]) only strings have UTF-8 byte length","object ({}) only strings have UTF-8 byte length","array ([1,2]) only strings have UTF-8 byte length","number (55) only strings have UTF-8 byte length","boolean (true) only strings have UTF-8 byte length","boolean (false) only strings have UTF-8 byte length"]
map(keys)
[{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}]
[[], ["abc","abcd","abcde"], ["x","y","z"]]
[1,2,empty,3,empty,4]
null
[1,2,3,4]
map(add)
[[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]]
[null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}]
map_values(.+1)
[0,1,2]
[1,2,3]
[add(null), add(range(range(10))), add(empty), add(10,range(10))]
null
[null,120,null,55]
# Real-world use case for add(empty)
.sum = add(.arr[])
{"arr":[]}
{"arr":[],"sum":null}
add({(.[]):1}) | keys
["a","a","b","a","d","b","d","a","d"]
["a","b","d"]
#
# User-defined functions
# Oh god.
#
def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g
3.0
106.0
105.0
def f: (1000,2000); f
123412345
1000
2000
def f(a;b;c;d;e;f): [a+1,b,c,d,e,f]; f(.[0];.[1];.[0];.[0];.[0];.[0])
[1,2]
[2,2,1,1,1,1]
def f: 1; def g: f, def f: 2; def g: 3; f, def f: g; f, g; def f: 4; [f, def f: g; def g: 5; f, g]+[f,g]
null
[4,1,2,3,3,5,4,1,2,3,3]
# Test precedence of 'def' vs '|'
def a: 0; . | a
null
0
# Many arguments
def f(a;b;c;d;e;f;g;h;i;j): [j,i,h,g,f,e,d,c,b,a]; f(.[0];.[1];.[2];.[3];.[4];.[5];.[6];.[7];.[8];.[9])
[0,1,2,3,4,5,6,7,8,9]
[9,8,7,6,5,4,3,2,1,0]
([1,2] + [4,5])
[1,2,3]
[1,2,4,5]
true
[1]
true
null,1,null
"hello"
null
1
null
[1,2,3]
[5,6]
[1,2,3]
[.[]|floor]
[-1.1,1.1,1.9]
[-2, 1, 1]
[.[]|sqrt]
[4,9]
[2,3]
(add / length) as $m | map((. - $m) as $d | $d * $d) | add / length | sqrt
[2,4,4,4,5,5,7,9]
2
# Should write a test that calls the -lm function from C (or bc(1)) to
# check that they match the corresponding jq functions. However,
# there's so little template code standing between that it suffices to
# test a handful of these. The results were checked by eye against
# bc(1).
atan * 4 * 1000000|floor / 1000000
1
3.141592
[(3.141592 / 2) * (range(0;20) / 20)|cos * 1000000|floor / 1000000]
null
[1,0.996917,0.987688,0.972369,0.951056,0.923879,0.891006,0.85264,0.809017,0.760406,0.707106,0.649448,0.587785,0.522498,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459]
[(3.141592 / 2) * (range(0;20) / 20)|sin * 1000000|floor / 1000000]
null
[0,0.078459,0.156434,0.233445,0.309016,0.382683,0.45399,0.522498,0.587785,0.649447,0.707106,0.760405,0.809016,0.85264,0.891006,0.923879,0.951056,0.972369,0.987688,0.996917]
def f(x): x | x; f([.], . + [42])
[1,2,3]
[[[1,2,3]]]
[[1,2,3],42]
[[1,2,3,42]]
[1,2,3,42,42]
# test multiple function arities and redefinition
def f: .+1; def g: f; def f: .+100; def f(a):a+.+11; [(g|f(20)), f]
1
[33,101]
# test closures and lexical scoping
def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x)
"more testing"
[1,100,2100.0,100,2100.0]
# test def f($a) syntax
def x(a;b): a as $a | b as $b | $a + $b; def y($a;$b): $a + $b; def check(a;b): [x(a;b)] == [y(a;b)]; check(.[];.[]*2)
[1,2,3]
true
# test backtracking through function calls and returns
# this test is *evil*
[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f]
999999999
[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]
# test recursion
def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
[1,2,3,4]
[1,2,6,24]
# test stack overflow and reallocation
# this test is disabled for now, it takes a realllllly long time.
# def f: if length > 1000 then . else .+[1]|f end; f | length
# []
# 1001
reduce .[] as $x (0; . + $x)
[1,2,4]
7
reduce .[] as [$i, {j:$j}] (0; . + $i - $j)
[[2,{"j":1}], [5,{"j":3}], [6,{"j":4}]]
5
reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j)
null
14
[-reduce -.[] as $x (0; . + $x)]
[1,2,3]
[6]
[reduce .[] / .[] as $i (0; . + $i)]
[1,2]
[4.5]
reduce .[] as $x (0; . + $x) as $x | $x
[1,2,3]
6
# This, while useless, should still compile.
reduce . as $n (.; .)
null
null
# Destructuring
. as {$a, b: [$c, {$d}]} | [$a, $c, $d]
{"a":1, "b":[2,{"d":3}]}
[1,2,3]
. as {$a, $b:[$c, $d]}| [$a, $b, $c, $d]
{"a":1, "b":[2,{"d":3}]}
[1,[2,{"d":3}],2,{"d":3}]
# Destructuring with alternation
.[] | . as {$a, b: [$c, {$d}]} ?// [$a, {$b}, $e] ?// $f | [$a, $b, $c, $d, $e, $f]
[{"a":1, "b":[2,{"d":3}]}, [4, {"b":5, "c":6}, 7, 8, 9], "foo"]
[1, null, 2, 3, null, null]
[4, 5, null, null, 7, null]
[null, null, null, null, null, "foo"]
# Destructuring DUP/POP issues
.[] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a
[[3],[4],[5],6]
# Runtime error: "jq: Cannot index array with string (\"c\")"
.[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a
[[3],[4],[5],6]
# Runtime error: "jq: Cannot index array with string (\"c\")"
[[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a
null
# Runtime error: "jq: Cannot index array with string (\"c\")"
[[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a
null
# Runtime error: "jq: Cannot index array with string (\"c\")"
.[] | . as {a:$a} ?// {a:$a} ?// $a | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
.[] as {a:$a} ?// {a:$a} ?// $a | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
[[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// $a | $a
null
[3]
[4]
[5]
6
[[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// $a | $a
null
[3]
[4]
[5]
6
.[] | . as {a:$a} ?// $a ?// {a:$a} | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
.[] as {a:$a} ?// $a ?// {a:$a} | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
[[3],[4],[5],6][] | . as {a:$a} ?// $a ?// {a:$a} | $a
null
[3]
[4]
[5]
6
[[3],[4],[5],6] | .[] as {a:$a} ?// $a ?// {a:$a} | $a
null
[3]
[4]
[5]
6
.[] | . as $a ?// {a:$a} ?// {a:$a} | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
.[] as $a ?// {a:$a} ?// {a:$a} | $a
[[3],[4],[5],6]
[3]
[4]
[5]
6
[[3],[4],[5],6][] | . as $a ?// {a:$a} ?// {a:$a} | $a
null
[3]
[4]
[5]
6
[[3],[4],[5],6] | .[] as $a ?// {a:$a} ?// {a:$a} | $a
null
[3]
[4]
[5]
6
. as $dot|any($dot[];not)
[1,2,3,4,true,false,1,2,3,4,5]
true
. as $dot|any($dot[];not)
[1,2,3,4,true]
false
. as $dot|all($dot[];.)
[1,2,3,4,true,false,1,2,3,4,5]
false
. as $dot|all($dot[];.)
[1,2,3,4,true]
true
# Check short-circuiting
any(true, error; .)
"badness"
true
all(false, error; .)
"badness"
false
any(not)
[]
false
all(not)
[]
true
any(not)
[false]
true
all(not)
[false]
true
[any,all]
[]
[false,true]
[any,all]
[true]
[true,true]
[any,all]
[false]
[false,false]
[any,all]
[true,false]
[true,false]
[any,all]
[null,null,true]
[true,false]
#
# Paths
#
path(.foo[0,1])
null
["foo", 0]
["foo", 1]
path(.[] | select(.>3))
[1,5,3]
[1]
path(.)
42
[]
try path(.a | map(select(.b == 0))) catch .
{"a":[{"b":0}]}
"Invalid path expression with result [{\"b\":0}]"
try path(.a | map(select(.b == 0)) | .[0]) catch .
{"a":[{"b":0}]}
"Invalid path expression near attempt to access element 0 of [{\"b\":0}]"
try path(.a | map(select(.b == 0)) | .c) catch .
{"a":[{"b":0}]}
"Invalid path expression near attempt to access element \"c\" of [{\"b\":0}]"
try path(.a | map(select(.b == 0)) | .[]) catch .
{"a":[{"b":0}]}
"Invalid path expression near attempt to iterate through [{\"b\":0}]"
path(.a[path(.b)[0]])
{"a":{"b":0}}
["a","b"]
[paths]
[1,[[],{"a":2}]]
[[0],[1],[1,0],[1,1],[1,1,"a"]]
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
{"bar": 42, "foo": ["a", "b", "c", "d"]}
"b"
{"bar": 42, "foo": ["a", 20, "c", "d"]}
{"bar": 42, "foo": ["a", "c", "d"]}
map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]]))
[[0], [0,1], [0,1,2]]
[null, null, 2]
[[0,null,42], [0,1,42], [0,1,42]]
[[0], [0,1], [0,1]]
map(delpaths([[0,"foo"]]))
[[{"foo":2, "x":1}], [{"bar":2}]]
[[{"x":1}], [{"bar":2}]]
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
{"bar":false}
null
{"bar":false, "foo": [null, 20]}
{"bar":false}
delpaths([[-200]])
[1,2,3]
[1,2,3]
try delpaths(0) catch .
{}
"Paths must be specified as an array"
del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x)
{"foo": [0,1,2,3,4], "bar": [0,1]}
null
{"foo": [0,1,2,3,4], "bar": [0,1]}
{"foo": [1,4], "bar": [1]}
{"bar": [1]}
del(.[1], .[-6], .[2], .[-3:9])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 3, 5, 6, 9]
del(.[nan])
[1,2,3]
[1,2,3]
del(.[nan,nan])
[1,2,3]
[1,2,3]
# negative index
setpath([-1]; 1)
[0]
[1]
pick(.a.b.c)
null
{"a":{"b":{"c":null}}}
pick(first)
[1,2]
[1]
pick(first|first)
[[10,20],30]
[[10]]
# negative indices in path expressions (since last/1 is .[-1])
try pick(last) catch .
[1,2]
"Out of bounds negative array index"
#
# Assignment
#
.message = "goodbye"
{"message": "hello"}
{"message": "goodbye"}
.foo = .bar
{"bar":42}
{"foo":42, "bar":42}
.foo |= .+1
{"foo": 42}
{"foo": 43}
.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2, .[] %=2
[1,3,5]
[3,5,7]
[2,6,10]
[-1,1,3]
[0.5, 1.5, 2.5]
[1,1,1]
[.[] % 7]
[-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7]
[0,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,0]
.foo += .foo
{"foo":2}
{"foo":4}
.[0].a |= {"old":., "new":(.+1)}
[{"a":1,"b":2}]
[{"a":{"old":1, "new":2},"b":2}]
def inc(x): x |= .+1; inc(.[].a)
[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}]
[{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}]
# #1358, getpath/1 should work in path expressions
.[] | try (getpath(["a",0,"b"]) |= 5) catch .
[null,{"b":0},{"a":0},{"a":null},{"a":[0,1]},{"a":{"b":1}},{"a":[{}]},{"a":[{"c":3}]}]
{"a":[{"b":5}]}
{"b":0,"a":[{"b":5}]}
"Cannot index number with number (0)"
{"a":[{"b":5}]}
"Cannot index number with string (\"b\")"
"Cannot index object with number (0)"
{"a":[{"b":5}]}
{"a":[{"c":3,"b":5}]}
# #2051, deletion using assigning empty against arrays
(.[] | select(. >= 2)) |= empty
[1,5,3,0,7]
[1,0]
.[] |= select(. % 2 == 0)
[0,1,2,3,4,5]
[0,2,4]
.foo[1,4,2,3] |= empty
{"foo":[0,1,2,3,4,5]}
{"foo":[0,5]}
.[2][3] = 1
[4]
[4, null, [null, null, null, 1]]
.foo[2].bar = 1
{"foo":[11], "bar":42}
{"foo":[11,null,{"bar":1}], "bar":42}
try ((map(select(.a == 1))[].b) = 10) catch .
[{"a":0},{"a":1}]
"Invalid path expression near attempt to iterate through [{\"a\":1}]"
try ((map(select(.a == 1))[].a) |= .+1) catch .
[{"a":0},{"a":1}]
"Invalid path expression near attempt to iterate through [{\"a\":1}]"
def x: .[1,2]; x=10
[0,1,2]
[0,10,10]
try (def x: reverse; x=10) catch .
[0,1,2]
"Invalid path expression with result [2,1,0]"
.[] = 1
[1,null,Infinity,-Infinity,NaN,-NaN]
[1,1,1,1,1,1]
#
# Conditionals
#
[.[] | if .foo then "yep" else "nope" end]
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
["yep","yep","yep","yep","nope","nope","yep","nope"]
[.[] | if .baz then "strange" elif .foo then "yep" else "nope" end]
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
["yep","yep","yep","yep","nope","nope","yep","nope"]
[if 1,null,2 then 3 else 4 end]
null
[3,4,3]
[if empty then 3 else 4 end]
null
[]
[if 1 then 3,4 else 5 end]
null
[3,4]
[if null then 3 else 5,6 end]
null
[5,6]
[if true then 3 end]
7
[3]
[if false then 3 end]
7
[7]
[if false then 3 else . end]
7
[7]
[if false then 3 elif false then 4 end]
7
[7]
[if false then 3 elif false then 4 else . end]
7
[7]
[-if true then 1 else 2 end]
null
[-1]
{x: if true then 1 else 2 end}
null
{"x":1}
if true then [.] else . end []
null
null
[.[] | [.foo[] // .bar]]
[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}]
[[1,2], [1], [3], [42], [41]]
.[] //= .[0]
["hello",true,false,[false],null]
["hello",true,"hello",[false],"hello"]
.[] | [.[0] and .[1], .[0] or .[1]]
[[true,[]], [false,1], [42,null], [null,false]]
[true,true]
[false,true]
[false,true]
[false,false]
[.[] | not]
[1,0,false,null,true,"hello"]
[false,false,true,true,false,false]
# Check numeric comparison binops
[10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20]
{}
[true,false,false,false,false,true]
[10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20]
{}
[true,true,false,false,true,true]
# And some in/equality tests
[ 10 == 10, 10 != 10, 10 != 11, 10 == 11]
{}
[true,false,true,false]
["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ]
{}
[true,false,false,true]
[[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]]
{}
[true,false,false,true]
[{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}]
{}
[true,false,true,false]
# ugly complicated thing
[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}]
{}
[true,false]
# containment operator
[("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))]
{}
[true, true, false]
# containment operator (embedded NULs!)
[contains(""), contains("\u0000")]
"\u0000"
[true, true]
[contains(""), contains("a"), contains("ab"), contains("c"), contains("d")]
"ab\u0000cd"
[true, true, true, true, true]
[contains("cd"), contains("b\u0000"), contains("ab\u0000")]
"ab\u0000cd"
[true, true, true]
[contains("b\u0000c"), contains("b\u0000cd"), contains("b\u0000cd")]
"ab\u0000cd"
[true, true, true]
[contains("@"), contains("\u0000@"), contains("\u0000what")]
"ab\u0000cd"
[false, false, false]
# Try/catch and general `?` operator
[.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .]
[0,1,2,3]
["foo","Cannot index number with string (\"a\")",3]
[.[]|(.a, .a)?]
[null,true,{"a":1}]
[null,null,1,1]
[[.[]|[.a,.a]]?]
[null,true,{"a":1}]
[]
[if error then 1 else 2 end?]
"foo"
[]
try error(0) // 1
null
1
# Lex token test #3589, like error(0)? // 1
error(0) ?// 1
null
1
1, try error(2), 3
null
1
3
1 + try 2 catch 3 + 4
null
7
[-try .]
1
[-1]
try -.? catch .
"foo"
"string (\"foo\") cannot be negated"
{x: try 1, y: try error catch 2, z: if true then 3 end}
null
{"x":1,"y":2,"z":3}
{x: 1 + 2, y: false or true, z: null // 3}
null
{"x":3,"y":true,"z":3}
.[] | try error catch .
[1,null,2]
1
null
2
try error("\($__loc__)") catch .
null
"{\"file\":\"<top-level>\",\"line\":1}"
# string operations
[.[]|startswith("foo")]
["fo", "foo", "barfoo", "foobar", "barfoob"]
[false, true, false, true, false]
[.[]|endswith("foo")]
["fo", "foo", "barfoo", "foobar", "barfoob"]
[false, true, true, false, false]
[.[] | split(", ")]
["a,b, c, d, e,f",", a,b, c, d, e,f, "]
[["a,b","c","d","e,f"],["","a,b","c","d","e,f",""]]
split("")
"abc"
["a","b","c"]
[.[]|ltrimstr("foo")]
["fo", "foo", "barfoo", "foobar", "afoo"]
["fo","","barfoo","bar","afoo"]
[.[]|rtrimstr("foo")]
["fo", "foo", "barfoo", "foobar", "foob"]
["fo","","bar","foobar","foob"]
[.[]|trimstr("foo")]
["fo", "foo", "barfoo", "foobarfoo", "foob"]
["fo","","bar","bar","b"]
[.[]|ltrimstr("")]
["a", "xx", ""]
["a", "xx", ""]
[.[]|rtrimstr("")]
["a", "xx", ""]
["a", "xx", ""]
[.[]|trimstr("")]
["a", "xx", ""]
["a", "xx", ""]
[(index(","), rindex(",")), indices(",")]
"a,bc,def,ghij,klmno"
[1,13,[1,4,8,13]]
[ index("aba"), rindex("aba"), indices("aba") ]
"xababababax"
[1,7,[1,3,5,7]]
# _strindices is used by indices/1 but is callable
try _strindices("abc") catch .
123
"number (123) cannot be searched, as it is not a string"
try _strindices(123) catch .
"abc"
"number (123) is not a string"
# trim
# \u000b is vertical tab (\v not supported by json)
map(trim), map(ltrim), map(rtrim)
[" \n\t\r\f\u000b", ""," ", "a", " a ", "abc", " abc ", " abc", "abc "]
["", "", "", "a", "a", "abc", "abc", "abc", "abc"]
["", "", "", "a", "a ", "abc", "abc ", "abc", "abc "]
["", "", "", "a", " a", "abc", " abc", " abc", "abc"]
trim, ltrim, rtrim
"\u0009\u000A\u000B\u000C\u000D\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000abc\u0009\u000A\u000B\u000C\u000D\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000"
"abc"
"abc\u0009\u000A\u000B\u000C\u000D\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000"
"\u0009\u000A\u000B\u000C\u000D\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000abc"
try trim catch ., try ltrim catch ., try rtrim catch .
123
"trim input must be a string"
"trim input must be a string"
"trim input must be a string"
indices(1)
[0,1,1,2,3,4,1,5]
[1,2,6]
indices([1,2])
[0,1,2,3,1,4,2,5,1,2,6,7]
[1,8]
indices([1,2])
[1]
[]
indices(", ")
"a,b, cd,e, fgh, ijkl"
[3,9,14]
index("!")
"здравствуй мир!"
14
.[:rindex("x")]
"正xyz"
"正"
indices("o")
"🇬🇧oo"
[2,3]
indices("o")
"ƒoo"
[1,2]
[.[]|split(",")]
["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"]
[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]]
[.[]|split(", ")]
["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"]
[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]]
[.[] * 3]
["a", "ab", "abc"]
["aaa", "ababab", "abcabcabc"]
[.[] * "abc"]
[-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 3.7, 10.0]
[null,null,"","","abc","abc","abcabcabc","abcabcabcabcabcabcabcabcabcabc"]
[. * (nan,-nan)]
"abc"
[null,null]
. * 100000 | [.[:10],.[-10:]]
"abc"
["abcabcabca","cabcabcabc"]
. * 1000000000
""
""
try (. * 1000000000) catch .
"abc"
"Repeat string result too long"
[.[] / ","]
["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"]
[["a"," bc"," def"," ghij"," jklmn"," a","b"," c","d"," e","f"],["a","b","c","d"," e","f","g","h"]]
[.[] / ", "]
["a, bc, def, ghij, jklmn, a,b, c,d, e,f", "a,b,c,d, e,f,g,h"]
[["a","bc","def","ghij","jklmn","a,b","c,d","e,f"],["a,b,c,d","e,f,g,h"]]
map(.[1] as $needle | .[0] | contains($needle))
[[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]]
[true, true, true, false, false]
map(.[1] as $needle | .[0] | contains($needle))
[[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]]
[true, true, false]
[({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))]
{}
[true, true, false]
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}})
{}
true
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}})
{}
false
sort
[42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null]
[null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}]
(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2)
[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}]
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}]
[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]]
[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]]
unique
[1,2,5,3,5,3,1,3]
[1,2,3,5]
unique
[]
[]
[min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])]
[[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]]
[[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]]
[min,max,min_by(.),max_by(.)]
[]
[null,null,null,null]
.foo[.baz]
{"foo":{"bar":4},"baz":"bar"}
4
.[] | .error = "no, it's OK"
[{"error":true}]
{"error": "no, it's OK"}
[{a:1}] | .[] | .a=999
null
{"a": 999}
to_entries
{"a": 1, "b": 2}
[{"key":"a", "value":1}, {"key":"b", "value":2}]
from_entries
[{"key":"a", "value":1}, {"Key":"b", "Value":2}, {"name":"c", "value":3}, {"Name":"d", "Value":4}]
{"a": 1, "b": 2, "c": 3, "d": 4}
with_entries(.key |= "KEY_" + .)
{"a": 1, "b": 2}
{"KEY_a": 1, "KEY_b": 2}
map(has("foo"))
[{"foo": 42}, {}]
[true, false]
map(has(2))
[[0,1], ["a","b","c"]]
[false, true]
has(nan)
[0,1,2]
false
has(infinite)
[0,1,2]
false
has(-infinite)
[0,1,2]
false
del(.[infinite])
[1,2,3]
[1,2,3]
del(.[-infinite])
[1,2,3]
[1,2,3]
has(1e10)
[0,1,2]
false
has(-1e10)
[0,1,2]
false
del(.[1e10])
[1,2,3]
[1,2,3]
del(.[-1e10])
[1,2,3]
[1,2,3]
keys
[42,3,35]
[0,1,2]
[][.]
1000000000000000000
null
map([1,2][0:.])
[-1, 1, 2, 3, 1000000000000000000]
[[1], [1], [1,2], [1,2], [1,2]]
# Test recursive object merge
{"k": {"a": 1, "b": 2}} * .
{"k": {"a": 0,"c": 3}}
{"k": {"a": 0, "b": 2, "c": 3}}
{"k": {"a": 1, "b": 2}, "hello": {"x": 1}} * .
{"k": {"a": 0,"c": 3}, "hello": 1}
{"k": {"a": 0, "b": 2, "c": 3}, "hello": 1}
{"k": {"a": 1, "b": 2}, "hello": 1} * .
{"k": {"a": 0,"c": 3}, "hello": {"x": 1}}
{"k": {"a": 0, "b": 2, "c": 3}, "hello": {"x": 1}}
{"a": {"b": 1}, "c": {"d": 2}, "e": 5} * .
{"a": {"b": 2}, "c": {"d": 3, "f": 9}}
{"a": {"b": 2}, "c": {"d": 3, "f": 9}, "e": 5}
[.[]|arrays]
[1,2,"foo",[],[3,[]],{},true,false,null]
[[],[3,[]]]
[.[]|objects]
[1,2,"foo",[],[3,[]],{},true,false,null]
[{}]
[.[]|iterables]
[1,2,"foo",[],[3,[]],{},true,false,null]
[[],[3,[]],{}]
[.[]|scalars]
[1,2,"foo",[],[3,[]],{},true,false,null]
[1,2,"foo",true,false,null]
[.[]|values]
[1,2,"foo",[],[3,[]],{},true,false,null]
[1,2,"foo",[],[3,[]],{},true,false]
[.[]|booleans]
[1,2,"foo",[],[3,[]],{},true,false,null]
[true,false]
[.[]|nulls]
[1,2,"foo",[],[3,[]],{},true,false,null]
[null]
flatten
[0, [1], [[2]], [[[3]]]]
[0, 1, 2, 3]
flatten(0)
[0, [1], [[2]], [[[3]]]]
[0, [1], [[2]], [[[3]]]]
flatten(2)
[0, [1], [[2]], [[[3]]]]
[0, 1, 2, [3]]
flatten(2)
[0, [1, [2]], [1, [[3], 2]]]
[0, 1, 2, 1, [3], 2]
try flatten(-1) catch .
[0, [1], [[2]], [[[3]]]]
"flatten depth must not be negative"
transpose
[[1], [2,3]]
[[1,2],[null,3]]
transpose
[]
[]
ascii_upcase
"useful but not for é"
"USEFUL BUT NOT FOR é"
bsearch(0,1,2,3,4)
[1,2,3]
-1
0
1
2
-4
bsearch({x:1})
[{ "x": 0 },{ "x": 1 },{ "x": 2 }]
1
try ["OK", bsearch(0)] catch ["KO",.]
"aa"
["KO","string (\"aa\") cannot be searched from"]
strftime("%Y-%m-%dT%H:%M:%SZ")
[2015,2,5,23,51,47,4,63]
"2015-03-05T23:51:47Z"
strftime("%A, %B %d, %Y")
1435677542.822351
"Tuesday, June 30, 2015"
strftime("%Y-%m-%dT%H:%M:%SZ")
[2024,2,15]
"2024-03-15T00:00:00Z"
mktime
[2024,8,21]
1726876800
gmtime
1425599507
[2015,2,5,23,51,47,4,63]
gmtime[5]
1425599507.25
47.25
# test invalid tm input
try strftime("%Y-%m-%dT%H:%M:%SZ") catch .
["a",1,2,3,4,5,6,7]
"strftime/1 requires parsed datetime inputs"
try strflocaltime("%Y-%m-%dT%H:%M:%SZ") catch .
["a",1,2,3,4,5,6,7]
"strflocaltime/1 requires parsed datetime inputs"
try mktime catch .
["a",1,2,3,4,5,6,7]
"mktime requires parsed datetime inputs"
# oss-fuzz #67403: non-string argument with number input fails assert
try ["OK", strftime([])] catch ["KO", .]
0
["KO","strftime/1 requires a string format"]
try ["OK", strflocaltime({})] catch ["KO", .]
0
["KO","strflocaltime/1 requires a string format"]
[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)]
"2015-03-05T23:51:47Z"
[[2015,2,5,23,51,47,4,63],1425599507]
# Check day-of-week and day of year computations
# (should trip an assert if this fails)
last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ"))
null
[2037,1,11,1,2,3,3,41]
# fromdateiso8601 accepts the calendar, ordinal and week date forms,
# in both the extended and the basic representation
[("2015-03-05T23:51:47Z", "20150305T235147Z", "2015-064T23:51:47Z", "2015064T235147Z", "2015-W10-4T23:51:47Z", "2015W104T235147Z") | fromdateiso8601]
null
[1425599507,1425599507,1425599507,1425599507,1425599507,1425599507]
# fractional seconds, with either decimal sign
[("2015-03-05T23:51:47.25Z", "2015-03-05T23:51:47,25Z", "20150305T235147.25Z") | fromdateiso8601]
null
[1425599507.25,1425599507.25,1425599507.25]
# UTC offsets; an absent offset means UTC
[("2015-03-06T08:51:47+09:00", "2015-03-06T08:51:47+0900", "2015-03-06T08:51:47+09", "2015-03-05T18:21:47-05:30", "2015-03-05T23:51:47z", "2015-03-05T23:51:47") | fromdateiso8601]
null
[1425599507,1425599507,1425599507,1425599507,1425599507,1425599507]
# the date and the time may be separated by "T", "t" or a space
[("2015-03-05t23:51:47Z", "2015-03-05 23:51:47Z") | fromdateiso8601]
null
[1425599507,1425599507]
# the W, T and Z designators may be written in lower case
[("2015-w10-4t23:51:47z", "2015w104t235147z", "2015-03-05t23:51:47z") | fromdateiso8601]
null
[1425599507,1425599507,1425599507]
# expanded years carry a sign and exactly six digits
[("+012015-03-05T23:51:47Z", "-000001-01-01T00:00:00Z", "+0020150305T235147Z", "+2025-03-05", "+02025-03-05", "+0002025-03-05") | try fromdateiso8601 catch "ERR"]
null
[316995119507,-62198755200,1425599507,"ERR","ERR","ERR"]
# "24:00:00" is midnight of the following day, at any precision
[("2015-03-05T24:00:00Z", "2015-03-05T24:00Z", "2015-03-05T24Z") | fromdateiso8601 | todate]
null
["2015-03-06T00:00:00Z","2015-03-06T00:00:00Z","2015-03-06T00:00:00Z"]
# leap seconds are dropped rather than rejected
"2015-06-30T23:59:60Z" | fromdateiso8601 | todate
null
"2015-06-30T23:59:59Z"
# 2015 starts on a Thursday, so it has 53 ISO weeks
"2015-W53-1T00:00:00Z" | fromdateiso8601 | todate
null
"2015-12-28T00:00:00Z"
# 2016 is a leap year starting on a Friday: 366 days, but only 52 ISO weeks
[("2016-366T00:00:00Z", "2016-W52-7T00:00:00Z") | fromdateiso8601 | todate]
null
["2016-12-31T00:00:00Z","2017-01-01T00:00:00Z"]
# fromdateiso8601 is not limited by the range of a 32-bit time_t (#3276)
fromdate
"2038-01-19T03:14:08Z"
2147483648
# the time may drop its seconds, or its minutes and seconds
[("2015-03-05T23:51Z", "2015-03-05T23Z", "20150305T2351Z", "20150305T23Z") | fromdateiso8601]
null
[1425599460,1425596400,1425599460,1425596400]
# a decimal fraction belongs to the lowest component present
[("2015-03-05T23:51:47,5Z", "2015-03-05T23:51,5Z", "2015-03-05T23,5Z") | fromdateiso8601]
null
[1425599507.5,1425599490,1425598200]
# a date on its own is midnight UTC
[("2015-03-05", "20150305", "2015-064", "2015064", "2015-W10-4", "2015W104") | fromdateiso8601]
null
[1425513600,1425513600,1425513600,1425513600,1425513600,1425513600]
# the zone designator belongs to the time, so a bare date cannot carry one
[("2015-03-05Z", "2015-03-05+09:00", "2015-03-05T") | try fromdateiso8601 catch "ERR"]
null
["ERR","ERR","ERR"]
# the date must be complete, and a time must not be empty
[("2015", "2015-03", "2015-W10", "2015-03T00:00:00Z", "T23:51:47Z", "T23:51:47", "23:51:47", "") | try fromdateiso8601 catch "ERR"]
null
["ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR"]
# the date and the time must agree on whether separators are used,
# and "YYYYMM" is ambiguous with "YYMMDD"
[("2015-03-05T235147Z", "20150305T23:51:47Z", "201503T00:00:00Z", "2015-03-05T2351Z", "20150305T23:51Z", "20150305T23205Z") | try fromdateiso8601 catch "ERR"]
null
["ERR","ERR","ERR","ERR","ERR","ERR"]
# out-of-range components are rejected
[("2015-02-30T00:00:00Z", "2015-13-01T00:00:00Z", "2015-00-01T00:00:00Z", "2015-366T00:00:00Z", "2016-W53-1T00:00:00Z", "2015-W10-8T00:00:00Z", "2015-W00-1T00:00:00Z", "2015-03-05T25:00:00Z", "2015-03-05T24:00:01Z", "2015-03-05T24:01Z", "2015-03-05T23:60:00Z", "2015-03-05T23:51:47+24:00", "2015-03-05T23:51:47+09:") | try fromdateiso8601 catch "ERR"]
null
["ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR","ERR"]
# the input must be exactly a datetime: no surrounding whitespace
[(" 2015-03-05T23:51:47Z", "2015-03-05T23:51:47Z ") | try fromdateiso8601 catch "ERR"]
null
["ERR","ERR"]
try fromdateiso8601 catch .
123
"fromdateiso8601 requires string inputs"
try fromdateiso8601 catch .
"not a date"
"date \"not a date\" is not a valid ISO 8601 datetime"
# module system
import "a" as foo; import "b" as bar; def fooa: foo::a; [fooa, bar::a, bar::b, foo::a]
null
["a","b","c","a"]
import "c" as foo; [foo::a, foo::c]
null
[0,"acmehbah"]
include "c"; [a, c]
null
[0,"acmehbah"]
import "data" as $e; import "data" as $d; [$d[].this,$e[].that,$d::d[].this,$e::e[].that]|join(";")
null
"is a test;is too;is a test;is too"
# Regression test for #2000
import "data" as $a; import "data" as $b; def f: {$a, $b}; f
null
{"a":[{"this":"is a test","that":"is too"}],"b":[{"this":"is a test","that":"is too"}]}
include "shadow1"; e
null
2
include "shadow1"; include "shadow2"; e
null
3
import "shadow1" as f; import "shadow2" as f; import "shadow1" as e; [e::e, f::e]
null
[2,3]
# A module reached only through another module must be linked before its
# importers, or dead-code elimination drops the definitions that only the
# importer's body references (#3597)
include "deep1"; f
null
[1,[21,3],[22,3,1]]
# ... including when the shared dependency is pulled in first by an importer
# whose own definitions end up unused, in either import order
import "deep2a" as a; import "deep2b" as b; b::f
null
[22,3,1]
import "deep2b" as b; import "deep2a" as a; b::f
null
[22,3,1]
%%FAIL
module (.+1); 0
jq: error: Module metadata must be constant at <top-level>, line 1, column 8:
module (.+1); 0
^^^^^
%%FAIL
module []; 0
jq: error: Module metadata must be an object at <top-level>, line 1, column 8:
module []; 0
^^
%%FAIL
include "a" (.+1); 0
jq: error: Module metadata must be constant at <top-level>, line 1, column 13:
include "a" (.+1); 0
^^^^^
%%FAIL
include "a" []; 0
jq: error: Module metadata must be an object at <top-level>, line 1, column 13:
include "a" []; 0
^^
%%FAIL
include "\ "; 0
jq: error: Invalid escape at line 1, column 4 (while parsing '"\ "') at <top-level>, line 1, column 10:
include "\ "; 0
^^
%%FAIL
include "\(a)"; 0
jq: error: Import path must be constant at <top-level>, line 1, column 9:
include "\(a)"; 0
^^^^^^
modulemeta
"c"
{"whatever":null,"deps":[{"as":"foo","is_data":false,"relpath":"a"},{"search":"./","as":"d","is_data":false,"relpath":"d"},{"search":"./","as":"d2","is_data":false,"relpath":"d"},{"search":"./../lib/jq","as":"e","is_data":false,"relpath":"e"},{"search":"./../lib/jq","as":"f","is_data":false,"relpath":"f"},{"as":"d","is_data":true,"relpath":"data"}],"defs":["a/0","c/0"]}
modulemeta | .deps | length
"c"
6
modulemeta | .defs | length
"c"
2
%%FAIL IGNORE MSG
import "syntaxerror" as e; .
jq: error: syntax error, unexpected ';', expecting end of file at tests/modules/syntaxerror/syntaxerror.jq, line 1, column 4:
wat;
^
%%FAIL
%::wat
jq: error: syntax error, unexpected '%', expecting end of file at <top-level>, line 1, column 1:
%::wat
^
import "test_bind_order" as check; check::check
null
true
try -. catch .
"very-long-long-long-long-string"
"string (\"very-long-long-long-long...\") cannot be negated"
try (.-.) catch .
"very-long-long-long-long-string"
"string (\"very-long-long-long-long...\") and string (\"very-long-long-long-long...\") cannot be subtracted"
"x" * range(0; 12; 2) + "☆" * 8 | try -. catch .
null
"string (\"☆☆☆☆☆☆☆☆\") cannot be negated"
"string (\"xx☆☆☆☆☆☆☆☆\") cannot be negated"
"string (\"xxxx☆☆☆☆☆☆...\") cannot be negated"
"string (\"xxxxxx☆☆☆☆☆☆...\") cannot be negated"
"string (\"xxxxxxxx☆☆☆☆☆...\") cannot be negated"
"string (\"xxxxxxxxxx☆☆☆☆...\") cannot be negated"
try (. + "x") catch . == if have_decnum then "number (12345678901234567890123456...) and string (\"x\") cannot be added" else "number (12345678901234568000000000...) and string (\"x\") cannot be added" end
123456789012345678901234567890
true
join(",")
["1",2,true,false,3.4]
"1,2,true,false,3.4"
.[] | join(",")
[[], [null], [null,null], [null,null,null]]
""
""
","
",,"
.[] | join(",")
[["a",null], [null,"a"]]
"a,"
",a"
try join(",") catch .
["1","2",{"a":{"b":{"c":33}}}]
"string (\"1,2,\") and object ({\"a\":{\"b\":{\"c\":33}}}) cannot be added"
try join(",") catch .
["1","2",[3,4,5]]
"string (\"1,2,\") and array ([3,4,5]) cannot be added"
join("-")
{"a":"x","b":1,"c":null,"d":false}
"x-1--false"
try join(1) catch .
["a","b"]
"string (\"a\") and number (1) cannot be added"
{if:0,and:1,or:2,then:3,else:4,elif:5,end:6,as:7,def:8,reduce:9,foreach:10,try:11,catch:12,label:13,import:14,include:15,module:16}
null
{"if":0,"and":1,"or":2,"then":3,"else":4,"elif":5,"end":6,"as":7,"def":8,"reduce":9,"foreach":10,"try":11,"catch":12,"label":13,"import":14,"include":15,"module":16}
try (1/.) catch .
0
"number (1) and number (0) cannot be divided because the divisor is zero"
try (1/0) catch .
0
"number (1) and number (0) cannot be divided because the divisor is zero"
try (0/0) catch .
0
"number (0) and number (0) cannot be divided because the divisor is zero"
try (1%.) catch .
0
"number (1) and number (0) cannot be divided (remainder) because the divisor is zero"
try (1%0) catch .
0
"number (1) and number (0) cannot be divided (remainder) because the divisor is zero"
# Basic numbers tests: integers, powers of two
[range(-52;52;1)] as $powers | [$powers[]|pow(2;.)|log2|round] == $powers
null
true
[range(-99/2;99/2;1)] as $orig | [$orig[]|pow(2;.)|log2] as $back | ($orig|keys)[]|. as $k | (($orig|.[$k])-($back|.[$k]))|if . < 0 then . * -1 else . end|select(.>.00005)
null
%%FAIL
{
jq: error: syntax error, unexpected end of file at <top-level>, line 1, column 1:
{
^
%%FAIL
}
jq: error: syntax error, unexpected INVALID_CHARACTER, expecting end of file at <top-level>, line 1, column 1:
}
^
(.[{}] = 0)?
null
INDEX(range(5)|[., "foo\(.)"]; .[0])
null
{"0":[0,"foo0"],"1":[1,"foo1"],"2":[2,"foo2"],"3":[3,"foo3"],"4":[4,"foo4"]}
JOIN({"0":[0,"abc"],"1":[1,"bcd"],"2":[2,"def"],"3":[3,"efg"],"4":[4,"fgh"]}; .[0]|tostring)
[[5,"foo"],[3,"bar"],[1,"foobar"]]
[[[5,"foo"],null],[[3,"bar"],[3,"efg"]],[[1,"foobar"],[1,"bcd"]]]
range(5;10)|IN(range(10))
null
true
true
true
true
true
range(5;13)|IN(range(0;10;3))
null
false
true
false
false
true
false
false
false
range(10;12)|IN(range(10))
null
false
false
IN(range(10;20); range(10))
null
false
IN(range(5;20); range(10))
null
true
# Regression test for #1347
(.a as $x | .b) = "b"
{"a":null,"b":null}
{"a":null,"b":"b"}
# Regression test for #1368
(.. | select(type == "object" and has("b") and (.b | type) == "array")|.b) |= .[0]
{"a": {"b": [1, {"b": 3}]}}
{"a": {"b": 1}}
isempty(empty)
null
true
isempty(range(3))
null
false
isempty(1,error("foo"))
null
false
# Regression test for #1815
index("")
""
null
# check that dead code removal occurs after builtin it generation
builtins|length > 10
null
true
"-1"|IN(builtins[] / "/"|.[1])
null
false
all(builtins[] / "/"; .[1]|tonumber >= 0)
null
true
builtins|any(.[:1] == "_")
null
false
## Test ability to use keywords (uncomment after eval is pushed)
#(.[] as $kw | "\"{\($kw)} as $\($kw) | $\($kw) | {$\($kw)} | {\($kw):.\($kw)}\""|eval|empty),null
#["as","def","module","import","include","if","then","else","elif","end","reduce","foreach","and","or","try","catch","label","break","__loc__"]
#null
#
#(.[] as $kw | "\"def f($\($kw)): $\($kw); f(.)\""|eval|empty),null
#["as","def","module","import","include","if","then","else","elif","end","reduce","foreach","and","or","try","catch","label","break","__loc__"]
#null
#
# Tests to cover the new toliteral number functionality
# For an example see #1652 and other linked issues
#
# We are backward and sanity compatible
map(. == 1)
[1, 1.0, 1.000, 100e-2, 1e+0, 0.0001e4]
[true, true, true, true, true, true]
# When no arithmetic is involved jq should preserve the literal value
.[0] | tostring | . == if have_decnum then "13911860366432393" else "13911860366432392" end
[13911860366432393]
true
.x | tojson | . == if have_decnum then "13911860366432393" else "13911860366432392" end
{"x":13911860366432393}
true
(13911860366432393 == 13911860366432392) | . == if have_decnum then false else true end
null
true
# Applying arithmetic to the value will truncate the result to double
. - 10
13911860366432393
13911860366432382
.[0] - 10
[13911860366432393]
13911860366432382
.x - 10
{"x":13911860366432393}
13911860366432382
# Unary negation preserves numerical precision
-. | tojson == if have_decnum then "-13911860366432393" else "-13911860366432392" end
13911860366432393
true
-. | tojson == if have_decnum then "0.12345678901234567890123456789" else "0.12345678901234568" end
-0.12345678901234567890123456789
true
[1E+1000,-1E+1000 | tojson] == if have_decnum then ["1E+1000","-1E+1000"] else ["1.7976931348623157e+308","-1.7976931348623157e+308"] end
null
true
. |= try . catch .
1
1
# decnum to double conversion
.[] as $n | $n+0 | [., tostring, . == $n]
[-9007199254740993, -9007199254740992, 9007199254740992, 9007199254740993, 13911860366432393]
[-9007199254740992,"-9007199254740992",true]
[-9007199254740992,"-9007199254740992",true]
[9007199254740992,"9007199254740992",true]
[9007199254740992,"9007199254740992",true]
[13911860366432392,"13911860366432392",true]
# abs, fabs, length
abs
"abc"
"abc"
map(abs)
[-0, 0, -10, -1.1]
[0,0,10,1.1]
map(fabs)
[-0, 0, -10, -1.1]
[0,0,10,1.1]
map(abs == length) | unique
[-10, -1.1, -1e-1, 1000000000000000002]
[true]
# The following is NOT prescriptive:
map(abs)
[0.1,1000000000000000002]
[1e-1, 1000000000000000002]
[1E+1000,-1E+1000 | abs | tojson] | unique == if have_decnum then ["1E+1000"] else ["1.7976931348623157e+308"] end
null
true
[1E+1000,-1E+1000 | length | tojson] | unique == if have_decnum then ["1E+1000"] else ["1.7976931348623157e+308"] end
null
true
# Using a keyword as variable/label name
123 as $label | $label
null
123
[ label $if | range(10) | ., (select(. == 5) | break $if) ]
null
[0,1,2,3,4,5]
reduce .[] as $then (4 as $else | $else; . as $elif | . + $then * $elif)
[1,2,3]
96
1 as $foreach | 2 as $and | 3 as $or | { $foreach, $and, $or, a }
{"a":4,"b":5}
{"foreach":1,"and":2,"or":3,"a":4}
[ foreach .[] as $try (1 as $catch | $catch - 1; . + $try; .) ]
[10,9,8,7]
[10,19,27,34]
# Object construction
{ a, $__loc__, c }
{"a":[1,2,3],"b":"foo","c":{"hi":"hey"}}
{"a":[1,2,3],"__loc__":{"file":"<top-level>","line":1},"c":{"hi":"hey"}}
1 as $x | "2" as $y | "3" as $z | { $x, as, $y: 4, ($z): 5, if: 6, foo: 7 }
{"as":8}
{"x":1,"as":8,"2":4,"3":5,"if":6,"foo":7}
# nan is parsed as a valid NaN value from JSON
fromjson | isnan
"nan"
true
tojson | fromjson
{"a":nan}
{"a":null}
# NaN with payload is not parsed
.[] | try (fromjson | isnan) catch .
["NaN","-NaN","NaN1","NaN10","NaN100","NaN1000","NaN10000","NaN100000"]
true
true
"Invalid numeric literal at EOF at line 1, column 4 (while parsing 'NaN1')"
"Invalid numeric literal at EOF at line 1, column 5 (while parsing 'NaN10')"
"Invalid numeric literal at EOF at line 1, column 6 (while parsing 'NaN100')"
"Invalid numeric literal at EOF at line 1, column 7 (while parsing 'NaN1000')"
"Invalid numeric literal at EOF at line 1, column 8 (while parsing 'NaN10000')"
"Invalid numeric literal at EOF at line 1, column 9 (while parsing 'NaN100000')"
# calling input/0, or debug/0 in a test doesn't crash jq
try input catch .
null
"break"
debug
1
1
# try/catch catches more than it should #1859
"foo" | try ((try . catch "caught too much") | error) catch "caught just right"
null
"caught just right"
.[]|(try (if .=="hi" then . else error end) catch empty) | "\(.) there!"
["hi","ho"]
"hi there!"
try (["hi","ho"]|.[]|(try . catch (if .=="ho" then "BROKEN"|error else empty end)) | if .=="ho" then error else "\(.) there!" end) catch "caught outside \(.)"
null
"hi there!"
"caught outside ho"
.[]|(try . catch (if .=="ho" then "BROKEN"|error else empty end)) | if .=="ho" then error else "\(.) there!" end
["hi","ho"]
"hi there!"
try (try error catch "inner catch \(.)") catch "outer catch \(.)"
"foo"
"inner catch foo"
try ((try error catch "inner catch \(.)")|error) catch "outer catch \(.)"
"foo"
"outer catch inner catch foo"
# Also #1859, but from #1885
first(.?,.?)
null
null
# Also #1859, but from #2140
{foo: "bar"} | .foo |= .?
null
{"foo": "bar"}
# Also #1859, but from #2220
. |= try 2
1
2
. |= try 2 catch 3
1
2
.[] |= try tonumber
["1", "2a", "3", " 4", "5 ", "6.7", ".89", "-876", "+5.43", 21]
[1, 3, 6.7, 0.89, -876, 5.43, 21]
# Also 1859, but from 2073
any(keys[]|tostring?;true)
{"a":"1","b":"2","c":"3"}
true
# explode/implode
# test replacement character (65533) for outside codepoint range and 0xd800 (55296) - 0xdfff (57343) utf16 surrogate pair range
# 1.1 and 1.9 to test round down of non-ints
implode|explode
[-1,0,1,2,3,1114111,1114112,55295,55296,57343,57344,1.1,1.9]
[65533,0,1,2,3,1114111,65533,55295,65533,65533,57344,1,1]
map(try implode catch .)
[123,["a"],[nan]]
["implode input must be an array","string (\"a\") can't be imploded, unicode codepoint needs to be numeric","number (null) can't be imploded, unicode codepoint needs to be numeric"]
try 0[implode] catch .
[]
"Cannot index number with string (\"\")"
# walk
walk(.)
{"x":0}
{"x":0}
walk(1)
{"x":0}
1
# The following is a regression test, not a requirement:
[walk(.,1)]
{"x":0}
[{"x":0},1]
# Issue #2584
walk(select(IN({}, []) | not))
{"a":1,"b":[]}
{"a":1}
# #2815
[range(10)] | .[1.2:3.5]
null
[1,2,3]
[range(10)] | .[1.5:3.5]
null
[1,2,3]
[range(10)] | .[1.7:3.5]
null
[1,2,3]
[range(10)] | .[1.7:4294967295]
null
[1,2,3,4,5,6,7,8,9]
[range(10)] | .[1.7:-4294967296]
null
[]
[[range(10)] | .[1.1,1.5,1.7]]
null
[1,1,1]
[range(5)] | .[1.1] = 5
null
[0,5,2,3,4]
[range(3)] | .[nan:1]
null
[0]
[range(3)] | .[1:nan]
null
[1,2]
[range(3)] | .[nan]
null
null
try ([range(3)] | .[nan] = 9) catch .
null
"Cannot set array element at NaN index"
try ("foobar" | .[1.5:3.5] = "xyz") catch .
null
"Cannot update string slices"
try ([range(10)] | .[1.5:3.5] = ["xyz"]) catch .
null
[0,"xyz",4,5,6,7,8,9]
try ("foobar" | .[1.5]) catch .
null
"Cannot index string with number (1.5)"
# setpath/2 does not leak the input after an invalid get #2970
try ["ok", setpath([1]; 1)] catch ["ko", .]
{"hi":"hello"}
["ko","Cannot index object with number (1)"]
try fromjson catch .
"{'a': 123}"
"Invalid string literal; expected \", but got ' at line 1, column 5 (while parsing '{'a': 123}')"
# ltrimstr/1 rtrimstr/1 don't leak on invalid input #2977
try ltrimstr(1) catch "x", try rtrimstr(1) catch "x" | "ok"
"hi"
"ok"
"ok"
try ltrimstr("x") catch "x", try rtrimstr("x") catch "x" | "ok"
{"hey":[]}
"ok"
"ok"
# ltrimstr/1 and rtrimstr/1 return an error for non-strings. #2969
.[] as [$x, $y] | try ["ok", ($x | ltrimstr($y))] catch ["ko", .]
[["hi",1],[1,"hi"],["hi","hi"],[1,1]]
["ko","startswith() requires string inputs"]
["ko","startswith() requires string inputs"]
["ok",""]
["ko","startswith() requires string inputs"]
.[] as [$x, $y] | try ["ok", ($x | rtrimstr($y))] catch ["ko", .]
[["hi",1],[1,"hi"],["hi","hi"],[1,1]]
["ko","endswith() requires string inputs"]
["ko","endswith() requires string inputs"]
["ok",""]
["ko","endswith() requires string inputs"]
# oss-fuzz #66061: setpath/2 leaks when indexing array with array
try ["OK", setpath([[1]]; 1)] catch ["KO", .]
[]
["KO","Cannot update field at array index of array"]
# regression test for #3227 (reduce)
[] | reduce .[] as $x (0, 1; .)
null
0
1
# regression test for #3227 (foreach)
foreach .[] as $x (0, 1; . + $x)
[1, 2]
1
3
2
4
# regression test for CVE-2025-49014 (use of fmt after free)
# tests with both empty string literal and empty string created by function
# as they seems to behave reference wise differently.
strflocaltime("" | ., @uri)
0
""
""
# regression tests for #3413
# upper range bounds should be in sync with the constants defined at
# src/jv_parse.c:#define MAX_PARSING_DEPTH (N)
# src/jv_print.c:#define MAX_PRINT_DEPTH (N)
# (N-1)
reduce range(9999) as $_ ([];[.]) | tojson | fromjson | flatten
null
[]
# (N)
reduce range(10000) as $_ ([];[.]) | tojson | try (fromjson) catch . | (contains("<skipped: too deep>") | not) and contains("Exceeds depth limit for parsing")
null
true
# (N+1)
reduce range(10001) as $_ ([];[.]) | tojson | contains("<skipped: too deep>")
null
true
# regression test for CVE-2026-33947
setpath([range(10000) | 0]; 0) | flatten
null
[0]
try setpath([range(10001) | 0]; 0) catch .
null
"Path too deep"
getpath([range(10000) | 0])
null
null
try getpath([range(10001) | 0]) catch .
null
"Path too deep"
delpaths([[range(10000) | 0]])
null
null
try delpaths([[range(10001) | 0]]) catch .
null
"Path too deep"
# regression test for CVE-2026-40612
reduce range(10000) as $_ ([]; [.]) | contains([[]])
null
true
try (reduce range(10001) as $_ ([]; [.]) as $x | $x | contains($x)) catch .
null
"Containment check too deep"
# regression test for CVE-2026-43896
reduce range(10000) as $_ ({}; {a: .}) as $x | $x * $x | length
null
1
try (reduce range(10001) as $_ ({}; {a: .}) as $x | $x * $x) catch .
null
"Object merge too deep"
# regression test for deep structural equality recursion
try ((reduce range(10001) as $_ ([]; [.])) as $x | (reduce range(10001) as $_ ([]; [.])) as $y | $x == $y) catch .
null
"Equality check too deep"
# regression tests for deep ordering comparisons
try ((reduce range(10001) as $_ ([]; [.])) as $x | [$x, $x] | sort) catch .
null
"Comparison too deep"
try ((reduce range(10001) as $_ ([]; [.])) as $x | [$x, $x] | unique) catch .
null
"Comparison too deep"
try ((reduce range(10001) as $_ ({}; {a: .})) as $x | [$x, $x] | sort) catch .
null
"Comparison too deep"
try ((reduce range(10001) as $_ ({}; {a: .})) as $x | [$x, $x] | unique) catch .
null
"Comparison too deep"