Tries to rewrite boolean comparisons. @param op operator @param cc compilation context @return optimized or original expression @throws QueryException query exception
(final CmpOp op, final CompileContext cc)
| 151 | * @throws QueryException query exception |
| 152 | */ |
| 153 | private Expr optBoolean(final CmpOp op, final CompileContext cc) throws QueryException { |
| 154 | final Expr expr1 = exprs[0], expr2 = exprs[1]; |
| 155 | final SeqType st1 = expr1.seqType(), st2 = expr2.seqType(); |
| 156 | if(st1.type == BasicType.BOOLEAN && st2.type == BasicType.BOOLEAN) { |
| 157 | final boolean eq = op == CmpOp.EQ, ne = op == CmpOp.NE; |
| 158 | if(expr2 instanceof Bln) { |
| 159 | final boolean ok = expr2 == Bln.TRUE, success = ne ^ ok; |
| 160 | |
| 161 | // boolean(A) = true() → boolean(A) |
| 162 | // boolean(A) <= true() → true() |
| 163 | if(st1.zeroOrOne() && (success || st1.one())) { |
| 164 | final Expr ex1 = st1.one() ? expr1 : cc.function(BOOLEAN, info, expr1); |
| 165 | final QuerySupplier<Expr> not = () -> cc.function(NOT, info, ex1); |
| 166 | return switch(op) { |
| 167 | case EQ -> ok ? ex1 : not.get(); |
| 168 | case NE -> ok ? not.get() : ex1; |
| 169 | case GE -> ok ? ex1 : Bln.TRUE; |
| 170 | case LE -> ok ? Bln.TRUE : not.get(); |
| 171 | case GT -> ok ? Bln.FALSE : ex1; |
| 172 | default -> ok ? not.get() : Bln.FALSE; |
| 173 | }; |
| 174 | } |
| 175 | |
| 176 | if(this instanceof CmpG) { |
| 177 | // (A, B) = true() → A or B |
| 178 | // (A, B) = false() → not(A and B) |
| 179 | final Expr[] args = expr1.args(); |
| 180 | if((eq || ne) && expr1 instanceof List && |
| 181 | ((Checks<Expr>) expr -> expr.seqType().eq(Types.BOOLEAN_O)).all(args)) { |
| 182 | return success ? new Or(info, args).optimize(cc) : |
| 183 | cc.function(NOT, info, new And(info, args).optimize(cc)); |
| 184 | } |
| 185 | |
| 186 | if(expr1 instanceof final SimpleMap map) { |
| 187 | final int al = args.length - 1; |
| 188 | final Expr last = args[al]; |
| 189 | |
| 190 | // expr ! true() = true() → exists(expr) |
| 191 | if(last instanceof final Bln bln && (eq || ne)) { |
| 192 | return bln.bool(info) != success ? Bln.FALSE : |
| 193 | cc.function(EXISTS, info, map.remove(cc, al)); |
| 194 | } |
| 195 | |
| 196 | final Expr[] ops = last.args(); |
| 197 | if(ops != null && ops.length > 0 && ops[0] instanceof ContextValue) { |
| 198 | if(last instanceof final CmpG cmp) { |
| 199 | // (name ! (. = 'Ukraine')) = true() → name = 'Ukraine' |
| 200 | // (code ! (. = 1)) = false() → code != 1 |
| 201 | final Expr op2 = ops[1]; |
| 202 | if(!op2.has(Flag.CTX) && (eq && ok || op2.seqType().one())) { |
| 203 | CmpOp cmpOp = cmp.op; |
| 204 | if(!success) cmpOp = cmpOp.invert(); |
| 205 | return new CmpG(info, map.remove(cc, al), op2, cmpOp).optimize(cc); |
| 206 | } |
| 207 | } else if(success && last instanceof final CmpR cmp) { |
| 208 | // (number ! (. >= 1e0) = true() → number >= 1e0 |
| 209 | return CmpR.get(cc, info, map.remove(cc, al), cmp.min, cmp.max); |
| 210 | } else if(success && last instanceof final CmpIR cmp) { |