Tries to rewrite fn:count. @param op operator @param cc compilation context @return optimized or original expression @throws QueryException query exception
(final CmpOp op, final CompileContext cc)
| 234 | * @throws QueryException query exception |
| 235 | */ |
| 236 | private Expr optCount(final CmpOp op, final CompileContext cc) throws QueryException { |
| 237 | final Expr expr1 = exprs[0]; |
| 238 | if(!COUNT.is(expr1)) return this; |
| 239 | |
| 240 | // distinct values checks |
| 241 | final Expr arg = expr1.arg(0), count = exprs[1]; |
| 242 | if(COUNT.is(count)) { |
| 243 | final Expr carg = count.arg(0); |
| 244 | // count(E) = count(distinct-values(E)) |
| 245 | if(DISTINCT_VALUES.is(carg) && arg.equals(carg.arg(0))) |
| 246 | return ((FnDistinctValues) carg).duplicates(op, cc); |
| 247 | // count(distinct-values(E)) = count(E) |
| 248 | if(DISTINCT_VALUES.is(arg) && arg.arg(0).equals(carg)) |
| 249 | return ((FnDistinctValues) arg).duplicates(op.swap(), cc); |
| 250 | } |
| 251 | // count(distinct-values(E)) = int |
| 252 | if(DISTINCT_VALUES.is(arg) && count instanceof final Itr itr) { |
| 253 | final long size1 = arg.arg(0).size(), size2 = itr.itr(); |
| 254 | if(size1 != -1 && size1 == size2) return ((FnDistinctValues) arg).duplicates(op.swap(), cc); |
| 255 | } |
| 256 | |
| 257 | final ExprList args = new ExprList(3); |
| 258 | if(count instanceof final ANum num) { |
| 259 | final double cnt = num.dbl(); |
| 260 | if(arg.seqType().zeroOrOne()) { |
| 261 | // count(ZeroOrOne) < 2 → true() |
| 262 | if(cnt > 1) { |
| 263 | return Bln.get(op.oneOf(CmpOp.LT, CmpOp.LE, CmpOp.NE)); |
| 264 | } |
| 265 | // count(ZeroOrOne) < 1 → empty(ZeroOrOne) |
| 266 | // count(ZeroOrOne) = 1 → exists(ZeroOrOne) |
| 267 | // count(ZeroOrOne) <= 1 → true() |
| 268 | if(cnt == 1) { |
| 269 | return op.oneOf(CmpOp.NE, CmpOp.LT) ? cc.function(EMPTY, info, arg) : |
| 270 | op.oneOf(CmpOp.EQ, CmpOp.GE) ? cc.function(EXISTS, info, arg) : |
| 271 | Bln.get(op == CmpOp.LE); |
| 272 | } |
| 273 | } |
| 274 | final long[] counts = countRange(op, cnt); |
| 275 | // count(A) >= 0 → true() |
| 276 | if(counts == COUNT_TRUE) return Bln.TRUE; |
| 277 | if(counts == COUNT_FALSE) return Bln.FALSE; |
| 278 | // count(A) > 0 → exists(A) |
| 279 | if(counts == COUNT_EMPTY) return cc.function(EMPTY, info, arg); |
| 280 | if(counts == COUNT_EXISTS) return cc.function(EXISTS, info, arg); |
| 281 | // count(A) > 1 → util:within(A, 2) |
| 282 | // count(A) < 5 → util:within(A, 0, 4) |
| 283 | if(counts != null) { |
| 284 | for(final long c : counts) args.add(Itr.get(c)); |
| 285 | } |
| 286 | } else if(op.oneOf(CmpOp.EQ, CmpOp.GE, CmpOp.LE)) { |
| 287 | final SeqType st2 = count.seqType(); |
| 288 | if(st2.type.instanceOf(BasicType.INTEGER)) { |
| 289 | if(count instanceof final RangeSeq rs) { |
| 290 | // count(A) = 3 to 5 → util:within(A, 3, 5) |
| 291 | args.add(Itr.get(rs.min())).add(Itr.get(rs.max())); |
| 292 | } else if(st2.one() && (count instanceof VarRef || count instanceof ContextValue)) { |
| 293 | // count(A) = $c → util:within(A, $c) |