| 49 | } |
| 50 | |
| 51 | @Override |
| 52 | public final Expr optimize(final CompileContext cc) throws QueryException { |
| 53 | // pre-evaluate if one value is empty: |
| 54 | // () eq local:expensive() → () |
| 55 | // void(123) = 1 → boolean(void('123')) |
| 56 | Expr expr = emptyExpr(); |
| 57 | if(expr != this) return cc.replaceWith(this, cc.function(BOOLEAN, info, expr)); |
| 58 | |
| 59 | // remove redundant type conversions |
| 60 | final Type t1 = exprs[0].seqType().type.atomic(), t2 = exprs[1].seqType().type.atomic(); |
| 61 | if(t1 != null && t2 != null) { |
| 62 | if(t1.isStringOrUntyped() && t2.isStringOrUntyped()) { |
| 63 | exprs = simplifyAll(Simplify.STRING, cc); |
| 64 | } else if(t1.isNumber() && t2.isNumber()) { |
| 65 | exprs = simplifyAll(Simplify.NUMBER, cc); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | // simplify operands |
| 70 | exprs = simplifyAll(Simplify.DISTINCT, cc); |
| 71 | |
| 72 | // swap operands |
| 73 | if(swap()) { |
| 74 | cc.info(QueryText.OPTSWAP_X, this); |
| 75 | Collections.reverse(Arrays.asList(exprs)); |
| 76 | op = op.swap(); |
| 77 | } |
| 78 | |
| 79 | // optimize expression |
| 80 | expr = opt(cc); |
| 81 | |
| 82 | // (if(A) then B else C) = X → if(A) then B = X else C = X |
| 83 | final Expr expr1 = exprs[0], expr2 = exprs[1]; |
| 84 | if(expr == this && expr1 instanceof final If iff && !expr1.has(Flag.NDT)) { |
| 85 | final Expr thn = new CmpG(info, iff.arg(0), expr2, op); |
| 86 | final Expr els = new CmpG(info, iff.arg(1), expr2.copy(cc, new IntObjectMap<>()), op); |
| 87 | return new If(info, iff.cond, thn.optimize(cc), els.optimize(cc)).optimize(cc); |
| 88 | } |
| 89 | |
| 90 | if(expr == this) expr = optArith(cc); |
| 91 | if(expr == this) expr = CmpIR.get(cc, this, false); |
| 92 | if(expr == this) expr = CmpR.get(cc, this); |
| 93 | if(expr == this) expr = CmpSR.get(cc, this); |
| 94 | if(expr == this) { |
| 95 | // skip runtime type check if items are known to be comparable |
| 96 | final SeqType st1 = expr1.seqType(), st2 = expr2.seqType(); |
| 97 | final Type type1 = st1.type, type2 = st2.type; |
| 98 | if(type1 == type2 && !type1.oneOf(BasicType.ANY_ATOMIC_TYPE, BasicType.ITEM) |
| 99 | || type1.isUntyped() || type2.isUntyped() |
| 100 | || type1.isNumber() && type2.isNumber() |
| 101 | || type1.isStringOrUntyped() && type2.isStringOrUntyped() |
| 102 | || type1.instanceOf(BasicType.BINARY) && type2.instanceOf(BasicType.BINARY) |
| 103 | || type1.instanceOf(BasicType.DURATION) && type2.instanceOf(BasicType.DURATION)) { |
| 104 | comparable = true; |
| 105 | } |
| 106 | |
| 107 | // choose best implementation |
| 108 | if(st1.zeroOrOne() && !st1.mayBeWrapped() && st2.zeroOrOne() && !st2.mayBeWrapped()) { |