Skip to content

Commit 9a4576e

Browse files
committed
Revert "reduce: handle empty updates (fix jqlang#1313)"
This reverts commit e24af3c. While the semantics are desirable, there is no way to implement them efficiently. The reason is that in order to handle backtracking (empty) from the state update expression, we have to retain a reference to the reduction state value in order to restore it upon backtracking. Retaining a reference to the reduction state kills performance by causing lots of additional memory allocations and garbage because the input to the update expression will always have at least two references, thus no changes to it can be done in-place, and all changes end up being CoW changes. Avoiding this is the very reason for the LOADVN instruction (leaving `null` in the variable loaded from).
1 parent b5560d8 commit 9a4576e

File tree

4 files changed

+4
-52
lines changed

4 files changed

+4
-52
lines changed

docs/content/3.manual/manual.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,18 +2596,10 @@ sections:
25962596
(2 as $item | . + $item) |
25972597
(1 as $item | . + $item)
25982598
2599-
If the reduction update expression outputs `empty` (that is,
2600-
no values), then the reduction state is left as-is. For
2601-
example, `reduce range(4) as $n ({}; if .==2 then empty else
2602-
.[$n|tostring] |= $n)` will produce `{"0":0,"1":1,"3":3}`.
2603-
26042599
examples:
26052600
- program: 'reduce .[] as $item (0; . + $item)'
26062601
input: '[10,2,5,3]'
26072602
output: ['20']
2608-
- program: 'reduce .[] as $n ([]; if $n%2==0 then empty else . + [$n] end)'
2609-
input: '[0,1,2,3,4,5]'
2610-
output: ['[1,3,5]']
26112603

26122604
- title: "`isempty(exp)`"
26132605
body: |

jq.1.prebuilt

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.\" generated with Ronn/v0.7.3
22
.\" http://github.com/rtomayko/ronn/tree/0.7.3
33
.
4-
.TH "JQ" "1" "May 2017" "" ""
4+
.TH "JQ" "1" "December 2017" "" ""
55
.
66
.SH "NAME"
77
\fBjq\fR \- Command\-line JSON processor
@@ -2100,7 +2100,7 @@ The \fBstrftime(fmt)\fR builtin formats a time (GMT) with the given format\. The
21002100
The format strings for \fBstrptime\fR and \fBstrftime\fR are described in typical C library documentation\. The format string for ISO 8601 datetime is \fB"%Y\-%m\-%dT%H:%M:%SZ"\fR\.
21012101
.
21022102
.P
2103-
jq may not support some or all of this date functionality on some systems\.
2103+
jq may not support some or all of this date functionality on some systems\. In particular, the \fB%u\fR and \fB%j\fR specifiers for \fBstrptime(fmt)\fR are not supported on macOS\.
21042104
.
21052105
.IP "" 4
21062106
.
@@ -2378,7 +2378,7 @@ break $out
23782378
.P
23792379
because no label \fB$out\fR is visible\.
23802380
.
2381-
.SS "Error Suppresion / Optional Operator: ?"
2381+
.SS "Error Suppression / Optional Operator: ?"
23822382
The \fB?\fR operator, used as \fBEXP?\fR, is shorthand for \fBtry EXP\fR\.
23832383
.
23842384
.IP "" 4
@@ -2861,25 +2861,10 @@ For each result that \fB\.[]\fR produces, \fB\. + $item\fR is run to accumulate
28612861
0 | (3 as $item | \. + $item) |
28622862
(2 as $item | \. + $item) |
28632863
(1 as $item | \. + $item)
2864-
.
2865-
.fi
2866-
.
2867-
.IP "" 0
2868-
.
2869-
.P
2870-
If the reduction update expression outputs \fBempty\fR (that is, no values), then the reduction state is left as\-is\. For example, \fBreduce range(4) as $n ({}; if \.==2 then empty else \.[$n|tostring] |= $n)\fR will produce \fB{"0":0,"1":1,"3":3}\fR\.
2871-
.
2872-
.IP "" 4
2873-
.
2874-
.nf
28752864

28762865
jq \'reduce \.[] as $item (0; \. + $item)\'
28772866
[10,2,5,3]
28782867
=> 20
2879-
2880-
jq \'reduce \.[] as $n ([]; if $n%2==0 then empty else \. + [$n] end)\'
2881-
[0,1,2,3,4,5]
2882-
=> [1,3,5]
28832868
.
28842869
.fi
28852870
.

src/compile.c

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -823,32 +823,12 @@ static block bind_alternation_matchers(block matchers, block body) {
823823

824824
block gen_reduce(block source, block matcher, block init, block body) {
825825
block res_var = gen_op_var_fresh(STOREV, "reduce");
826-
block update_var = gen_op_bound(STOREV, res_var);
827-
block jmp = gen_op_targetlater(JUMP);
828-
if (body.last == NULL) {
829-
inst_set_target(jmp, jmp);
830-
} else {
831-
inst_set_target(jmp, body);
832-
}
833826
block loop = BLOCK(gen_op_simple(DUPN),
834827
source,
835828
bind_alternation_matchers(matcher,
836829
BLOCK(gen_op_bound(LOADVN, res_var),
837-
/*
838-
* We fork to the body, jump to
839-
* the STOREV. This means that
840-
* if body produces no results
841-
* (i.e., it just does empty)
842-
* then we keep the current
843-
* reduction state as-is.
844-
*
845-
* To break out of a
846-
* reduction... use break.
847-
*/
848-
gen_op_target(FORK, jmp),
849-
jmp,
850830
body,
851-
update_var)),
831+
gen_op_bound(STOREV, res_var))),
852832
gen_op_simple(BACKTRACK));
853833
return BLOCK(gen_op_simple(DUP),
854834
init,

tests/jq.test

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -705,11 +705,6 @@ reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j)
705705
null
706706
14
707707

708-
# Test fix for #1313 (reduce should handle empty updates)
709-
reduce range(5) as $n ([]; select($n%2 == 1) | . + [$n])
710-
null
711-
[1,3]
712-
713708
# This, while useless, should still compile.
714709
reduce . as $n (.; .)
715710
null

0 commit comments

Comments
 (0)