Example #1
0
 @Override
 public final boolean has(final Flag flag) {
   // first step or root expression will be used as context
   if (flag == Flag.CTX) return root == null || root.has(flag);
   for (final Expr s : steps) if (s.has(flag)) return true;
   return root != null && root.has(flag);
 }
Example #2
0
 /**
  * Ensures that none of the specified expressions performs an update. Otherwise, throws an
  * exception.
  *
  * @param exprs expressions (may be {@code null}, and may contain {@code null} references)
  * @throws QueryException query exception
  */
 protected final void checkNoneUp(final Expr... exprs) throws QueryException {
   if (exprs == null) return;
   checkAllUp(exprs);
   for (final Expr expr : exprs) {
     if (expr != null && expr.has(Flag.UPD)) throw UPNOT_X.get(info, description());
   }
 }
Example #3
0
 @Override
 public boolean has(final Flag flag) {
   for (final Expr pred : preds) {
     if (flag == Flag.FCS && pred.seqType().mayBeNumber() || pred.has(flag)) return true;
   }
   return false;
 }
Example #4
0
 @Override
 public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException {
   // number of predicates may change in loop
   for (int p = 0; p < preds.length; p++) {
     final Expr pred = preds[p];
     if (pred instanceof CmpG || pred instanceof CmpV) {
       final Cmp cmp = (Cmp) pred;
       if (cmp.exprs[0].isFunction(Function.POSITION)) {
         final Expr e2 = cmp.exprs[1];
         final SeqType st2 = e2.seqType();
         // position() = last() -> last()
         // position() = $n (numeric) -> $n
         if (e2.isFunction(Function.LAST) || st2.one() && st2.type.isNumber()) {
           if (cmp instanceof CmpG && ((CmpG) cmp).op == OpG.EQ
               || cmp instanceof CmpV && ((CmpV) cmp).op == OpV.EQ) {
             qc.compInfo(OPTWRITE, pred);
             preds[p] = e2;
           }
         }
       }
     } else if (pred instanceof And) {
       if (!pred.has(Flag.FCS)) {
         // replace AND expression with predicates (don't swap position tests)
         qc.compInfo(OPTPRED, pred);
         final Expr[] and = ((Arr) pred).exprs;
         final int m = and.length - 1;
         final ExprList el = new ExprList(preds.length + m);
         for (final Expr e : Arrays.asList(preds).subList(0, p)) el.add(e);
         for (final Expr a : and) {
           // wrap test with boolean() if the result is numeric
           el.add(Function.BOOLEAN.get(null, info, a).optimizeEbv(qc, scp));
         }
         for (final Expr e : Arrays.asList(preds).subList(p + 1, preds.length)) el.add(e);
         preds = el.finish();
       }
     } else if (pred instanceof ANum) {
       final ANum it = (ANum) pred;
       final long i = it.itr();
       if (i == it.dbl()) {
         preds[p] = Pos.get(i, info);
       } else {
         qc.compInfo(OPTREMOVE, this, pred);
         return Empty.SEQ;
       }
     } else if (pred.isValue()) {
       if (pred.ebv(qc, info).bool(info)) {
         qc.compInfo(OPTREMOVE, this, pred);
         preds = Array.delete(preds, p--);
       } else {
         // handle statically known predicates
         qc.compInfo(OPTREMOVE, this, pred);
         return Empty.SEQ;
       }
     }
   }
   return this;
 }
Example #5
0
 /**
  * Ensures that all specified expressions are vacuous or either updating or non-updating.
  * Otherwise, throws an exception.
  *
  * @param exprs expressions to be checked
  * @throws QueryException query exception
  */
 void checkAllUp(final Expr... exprs) throws QueryException {
   // updating state: 0 = initial state, 1 = updating, -1 = non-updating
   int s = 0;
   for (final Expr expr : exprs) {
     expr.checkUp();
     if (expr.isVacuous()) continue;
     final boolean u = expr.has(Flag.UPD);
     if (u && s == -1 || !u && s == 1) throw UPALL.get(info, description());
     s = u ? 1 : -1;
   }
 }
Example #6
0
  /**
   * Merges expensive descendant-or-self::node() steps.
   *
   * @param qc query context
   * @return original or new expression
   */
  private Expr mergeSteps(final QueryContext qc) {
    boolean opt = false;
    final int sl = steps.length;
    final ExprList stps = new ExprList(sl);
    for (int s = 0; s < sl; s++) {
      final Expr step = steps[s];
      // check for simple descendants-or-self step with succeeding step
      if (s < sl - 1 && step instanceof Step) {
        final Step curr = (Step) step;
        if (curr.simple(DESCORSELF, false)) {
          // check succeeding step
          final Expr next = steps[s + 1];
          // descendant-or-self::node()/child::X -> descendant::X
          if (simpleChild(next)) {
            ((Step) next).axis = DESC;
            opt = true;
            continue;
          }
          // descendant-or-self::node()/(X, Y) -> (descendant::X | descendant::Y)
          Expr e = mergeList(next);
          if (e != null) {
            steps[s + 1] = e;
            opt = true;
            continue;
          }
          // //(X, Y)[text()] -> (/descendant::X | /descendant::Y)[text()]
          if (next instanceof Filter && !next.has(Flag.FCS)) {
            final Filter f = (Filter) next;
            e = mergeList(f.root);
            if (e != null) {
              f.root = e;
              opt = true;
              continue;
            }
          }
        }
      }
      stps.add(step);
    }

    if (opt) {
      qc.compInfo(OPTDESC);
      return get(info, root, stps.finish());
    }
    return this;
  }
Example #7
0
  /**
   * Merges a single predicate with the root expression and returns the resulting expression, or
   * returns a self reference if the expression cannot be merged. This function is e.g. called by
   * {@link Where#optimize}.
   *
   * @param qc query context
   * @param scp variable scope
   * @param root root expression
   * @return expression
   * @throws QueryException query exception
   */
  public Expr merge(final Expr root, final QueryContext qc, final VarScope scp)
      throws QueryException {

    // only one predicate can be rewritten; root expression must yield nodes
    if (preds.length != 1 || !(root.seqType().type instanceof NodeType)) return this;

    final Expr pred = preds[0];
    // a[.] -> a
    if (pred instanceof Context) return root;

    if (!pred.seqType().mayBeNumber()) {
      // a[b] -> a/b
      if (pred instanceof Path) return Path.get(info, root, pred).optimize(qc, scp);

      if (pred instanceof CmpG) {
        final CmpG cmp = (CmpG) pred;
        final Expr expr1 = cmp.exprs[0], expr2 = cmp.exprs[1];
        // only first operand can depend on context
        if (!expr2.has(Flag.CTX)) {
          Expr path = null;
          // a[. = 'x'] -> a = 'x'
          if (expr1 instanceof Context) path = root;
          // a[text() = 'x'] -> a/text() = 'x'
          if (expr1 instanceof Path) path = Path.get(info, root, expr1).optimize(qc, scp);
          if (path != null) return new CmpG(path, expr2, cmp.op, cmp.coll, cmp.sc, cmp.info);
        }
      }

      if (pred instanceof FTContains) {
        final FTContains cmp = (FTContains) pred;
        final FTExpr ftexpr = cmp.ftexpr;
        // only first operand can depend on context
        if (!ftexpr.has(Flag.CTX)) {
          final Expr expr = cmp.expr;
          Expr path = null;
          // a[. contains text 'x'] -> a contains text 'x'
          if (expr instanceof Context) path = root;
          // [text() contains text 'x'] -> a/text() contains text 'x'
          if (expr instanceof Path) path = Path.get(info, root, expr).optimize(qc, scp);
          if (path != null) return new FTContains(path, ftexpr, cmp.info);
        }
      }
    }
    return this;
  }
Example #8
0
 /**
  * Ensures that the specified expression performs no updates. Otherwise, throws an exception.
  *
  * @param expr expression (may be {@code null})
  * @throws QueryException query exception
  */
 protected void checkNoUp(final Expr expr) throws QueryException {
   if (expr == null) return;
   expr.checkUp();
   if (expr.has(Flag.UPD)) throw UPNOT_X.get(info, description());
 }