Skip to content

Commit 8766e9f

Browse files
[MOD] XQuery, nondeterminism: existence checks, counts
1 parent da08d1d commit 8766e9f

4 files changed

Lines changed: 47 additions & 11 deletions

File tree

basex-core/src/main/java/org/basex/query/func/fn/FnCount.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
* @author Christian Gruen
2020
*/
2121
public final class FnCount extends StandardFunc {
22+
/** Nondeterministic input. */
23+
private boolean ndt;
24+
2225
@Override
2326
public Itr item(final QueryContext qc, final InputInfo ii) throws QueryException {
2427
// if the iterator size is unknown or nondeterministic, iterate through all results
25-
final Expr expr = arg(0);
26-
final Iter input = expr.iter(qc);
27-
long size = expr.has(Flag.NDT) ? -1 : input.size();
28+
final Iter input = arg(0).iter(qc);
29+
long size = ndt ? -1 : input.size();
2830
if(size == -1) {
2931
do ++size; while(qc.next(input) != null);
3032
}
@@ -38,10 +40,12 @@ protected void simplifyArgs(final CompileContext cc) throws QueryException {
3840

3941
@Override
4042
protected Expr opt(final CompileContext cc) throws QueryException {
41-
// return static result size
4243
final Expr input = arg(0);
44+
ndt = input.has(Flag.NDT);
45+
46+
// return static result size
4347
final long size = input.size();
44-
if(size >= 0 && !input.has(Flag.NDT)) return Itr.get(size);
48+
if(size >= 0 && !ndt) return Itr.get(size);
4549

4650
// count(map:keys(E)) → map:size(E)
4751
if(_MAP_KEYS.is(input))

basex-core/src/main/java/org/basex/query/func/fn/FnEmpty.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ protected final Expr opt(final CompileContext cc) throws QueryException {
5454
if(REPLICATE.is(input) && input.arg(1) instanceof Itr) {
5555
input = input.arg(0);
5656
}
57+
// exists(head(E)) → exists(E), empty(head(E)) → empty(E)
58+
if(HEAD.is(input)) {
59+
input = input.arg(0);
60+
}
5761
// rewrite list to union expression: exists((nodes1, nodes2)) → exists(nodes1 | nodes2)
5862
if(input instanceof List && input.seqType().type instanceof NodeType) {
5963
input = new Union(info, input.args()).optimize(cc);
@@ -84,6 +88,13 @@ protected final Expr opt(final CompileContext cc) throws QueryException {
8488
return new CmpG(info, input, Itr.ZERO, exists ? CmpOp.NE : CmpOp.EQ).optimize(cc);
8589
}
8690

91+
// exists(tail(E)) → util:count-within(E, 2), empty(tail(E)) → util:count-within(E, 0, 1)
92+
if(TAIL.is(input) && !input.has(Flag.NDT)) {
93+
final Expr seq = input.arg(0);
94+
return exists ? cc.function(_UTIL_COUNT_WITHIN, info, seq, Itr.get(2)) :
95+
cc.function(_UTIL_COUNT_WITHIN, info, seq, Itr.ZERO, Itr.ONE);
96+
}
97+
8798
return embed(cc, true);
8899
}
89100

basex-core/src/main/java/org/basex/query/func/util/UtilCountWithin.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
* @author Christian Gruen
2121
*/
2222
public final class UtilCountWithin extends StandardFunc {
23+
/** Nondeterministic input. */
24+
private boolean ndt;
25+
2326
@Override
2427
public Bln item(final QueryContext qc, final InputInfo ii) throws QueryException {
2528
return Bln.get(test(qc, info, 0));
@@ -32,12 +35,10 @@ public boolean test(final QueryContext qc, final InputInfo ii, final long pos)
3235
final long min = minMax[0], max = minMax[1];
3336

3437
// iterative through the results if an iterator is nondeterministic or its size is unknown
35-
final Expr expr = arg(0);
36-
final Iter input = expr.iter(qc);
37-
final boolean enforce = expr.has(Flag.NDT);
38-
long size = enforce ? -1 : input.size();
38+
final Iter input = arg(0).iter(qc);
39+
long size = ndt ? -1 : input.size();
3940
if(size == -1) {
40-
if(enforce) {
41+
if(ndt) {
4142
do ++size; while(qc.next(input) != null);
4243
} else if(max == Long.MAX_VALUE) {
4344
// >= min: skip if minimum is reached
@@ -58,10 +59,11 @@ protected void simplifyArgs(final CompileContext cc) throws QueryException {
5859
@Override
5960
protected Expr opt(final CompileContext cc) throws QueryException {
6061
final Expr input = arg(0);
62+
ndt = input.has(Flag.NDT);
6163

6264
// skip rewrites for nondeterministic input, which must be fully evaluated for its side effects
6365
final long[] minMax = minMaxValues(cc.qc);
64-
if(minMax != null && !input.has(Flag.NDT)) {
66+
if(minMax != null && !ndt) {
6567
final long min = minMax[0], max = minMax[1];
6668
if(min > max) return Bln.FALSE;
6769
if(min <= 0 && max == Long.MAX_VALUE) return Bln.TRUE;

basex-core/src/test/java/org/basex/query/func/FnModuleTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,25 @@ public final class FnModuleTest extends SandboxTest {
16641664
error(func.args("", ""), HASH_ALGORITHM_X);
16651665
}
16661666

1667+
/** Tests the fn:empty and fn:exists rewritings of fn:head and fn:tail arguments. */
1668+
@Test public void emptyExistsHeadTail() {
1669+
final String seq = " tokenize(" + wrap("a b c") + ", ' ')";
1670+
check(EXISTS.args(HEAD.args(seq)), true, root(EXISTS), empty(HEAD));
1671+
check(EMPTY.args(HEAD.args(seq)), false, root(EMPTY), empty(HEAD));
1672+
check(EXISTS.args(TAIL.args(seq)), true, root(_UTIL_COUNT_WITHIN), empty(TAIL));
1673+
check(EMPTY.args(TAIL.args(seq)), false, root(_UTIL_COUNT_WITHIN), empty(TAIL));
1674+
check(EXISTS.args(SUBSEQUENCE.args(seq, 2)), true, root(_UTIL_COUNT_WITHIN), empty(TAIL));
1675+
1676+
final String one = " tokenize(" + wrap("a") + ", ' ')";
1677+
check(EXISTS.args(TAIL.args(one)), false, root(_UTIL_COUNT_WITHIN));
1678+
check(EMPTY.args(TAIL.args(one)), true, root(_UTIL_COUNT_WITHIN));
1679+
1680+
// head is rewritten even for nondeterministic input; tail is kept
1681+
final String ndt = " (1 to 3) ! (if(. = 3) then error() else .)";
1682+
check(EXISTS.args(HEAD.args(ndt)), true, empty(HEAD));
1683+
check(EXISTS.args(TAIL.args(ndt)), true, exists(TAIL));
1684+
}
1685+
16671686
/** Test method. */
16681687
@Test public void head() {
16691688
final Function func = HEAD;

0 commit comments

Comments
 (0)