Example #1
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 #2
0
  @Override
  public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException {
    final Expr c = super.optimize(qc, scp);
    if (c != this) return c;

    final int es = exprs.length;
    final ExprList list = new ExprList(es);
    for (int i = 0; i < es; i++) {
      Expr e = exprs[i];
      if (e instanceof CmpG) {
        // merge adjacent comparisons
        while (i + 1 < es && exprs[i + 1] instanceof CmpG) {
          final Expr tmp = ((CmpG) e).union((CmpG) exprs[i + 1], qc, scp);
          if (tmp != null) {
            e = tmp;
            i++;
          } else {
            break;
          }
        }
      }
      // expression will always return true
      if (e == Bln.TRUE) return optPre(Bln.TRUE, qc);
      // skip expression yielding false
      if (e != Bln.FALSE) list.add(e);
    }

    // all arguments return false
    if (list.isEmpty()) return optPre(Bln.FALSE, qc);

    if (es != list.size()) {
      qc.compInfo(OPTREWRITE_X, this);
      exprs = list.finish();
    }
    compFlatten(qc);

    boolean not = true;
    for (final Expr expr : exprs) {
      if (!expr.isFunction(Function.NOT)) {
        not = false;
        break;
      }
    }

    if (not) {
      qc.compInfo(OPTREWRITE_X, this);
      final int el = exprs.length;
      final Expr[] inner = new Expr[el];
      for (int e = 0; e < el; e++) inner[e] = ((Arr) exprs[e]).exprs[0];
      final Expr ex = new And(info, inner).optimize(qc, scp);
      return Function.NOT.get(null, info, ex).optimize(qc, scp);
    }

    // return single expression if it yields a boolean
    return exprs.length == 1 ? compBln(exprs[0], info) : this;
  }
Example #3
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 #4
0
 @Override
 public boolean indexAccessible(final IndexInfo ii) throws QueryException {
   int costs = 0;
   final ExprList el = new ExprList(exprs.length);
   for (final Expr expr : exprs) {
     // check if expression can be rewritten, and if access is not sequential
     if (!expr.indexAccessible(ii)) return false;
     // skip expressions without results
     if (ii.costs == 0) continue;
     costs += ii.costs;
     el.add(ii.expr);
   }
   // use summarized costs for estimation
   ii.costs = costs;
   // no expressions means no costs: expression will later be ignored
   ii.expr = el.size() == 1 ? el.get(0) : new Union(info, el.finish());
   return true;
 }
Example #5
0
  /**
   * Returns a new path instance. A path implementation is chosen that works fastest for the given
   * steps.
   *
   * @param info input info
   * @param root root expression; can be {@code null}
   * @param steps steps
   * @return path instance
   */
  public static Path get(final InputInfo info, final Expr root, final Expr... steps) {
    // new list with steps
    final ExprList stps = new ExprList(steps.length);

    // merge nested paths
    Expr rt = root;
    if (rt instanceof Path) {
      final Path path = (Path) rt;
      stps.add(path.steps);
      rt = path.root;
    }
    // remove redundant context reference
    if (rt instanceof Context) rt = null;

    // add steps of input array
    final int sl = steps.length;
    for (int s = 0; s < sl; s++) {
      Expr step = steps[s];
      if (step instanceof Context) {
        // remove redundant context references
        if (sl > 1) continue;
        // single step: rewrite to axis step (required to sort results of path)
        step = Step.get(((Context) step).info, SELF, Test.NOD);
      } else if (step instanceof Filter) {
        // rewrite to axis step (can be evaluated faster than filter expression)
        final Filter f = (Filter) step;
        if (f.root instanceof Context) step = Step.get(f.info, SELF, Test.NOD, f.preds);
      }
      stps.add(step);
    }

    // check if all steps are axis steps
    boolean axes = true;
    final Expr[] st = stps.finish();
    for (final Expr step : st) axes &= step instanceof Step;

    // choose best implementation
    return axes
        ? iterative(rt, st) ? new IterPath(info, rt, st) : new CachedPath(info, rt, st)
        : new MixedPath(info, rt, st);
  }
Example #6
0
  @Override
  public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException {
    super.optimize(qc, scp);

    final ExprList el = new ExprList(exprs.length);
    for (final Expr ex : exprs) {
      if (ex.isEmpty()) {
        // remove empty operands (return empty sequence if first value is empty)
        if (el.isEmpty()) return optPre(qc);
        qc.compInfo(OPTREMOVE_X_X, this, ex);
      } else {
        el.add(ex);
      }
    }
    // ensure that results are always sorted
    if (el.size() == 1 && iterable) return el.get(0);
    // replace expressions with optimized list
    exprs = el.finish();
    return this;
  }
Example #7
0
  /**
   * Returns an equivalent expression which accesses an index. If the expression cannot be
   * rewritten, the original expression is returned.
   *
   * <p>The following types of queries can be rewritten (in the examples, the equality comparison is
   * used, which will be rewritten to {@link ValueAccess} instances):
   *
   * <pre>
   * 1. A[text() = '...']    -> IA('...')
   * 2. A[. = '...']         -> IA('...', A)
   * 3. text()[. = '...']    -> IA('...')
   * 4. A[B = '...']         -> IA('...', B)/parent::A
   * 1. A[B/text() = '...']  -> IA('...')/parent::B/parent::A
   * 2. A[B/C = '...']       -> IA('...', C)/parent::B/parent::A
   * 7. A[@a = '...']        -> IA('...', @a)/parent::A
   * 8. @a[. = '...']        -> IA('...', @a)</pre>
   *
   * Queries of type 1, 3, 5 will not yield any results if the string to be compared is empty.
   *
   * @param qc query context
   * @param rt root value
   * @return original or new expression
   * @throws QueryException query exception
   */
  private Expr index(final QueryContext qc, final Value rt) throws QueryException {
    // only rewrite paths with data reference
    final Data data = rt.data();
    if (data == null) return this;

    // cache index access costs
    IndexInfo index = null;
    // cheapest predicate and step
    int iPred = 0, iStep = 0;

    // check if path can be converted to an index access
    final int sl = steps.length;
    for (int s = 0; s < sl; s++) {
      // only accept descendant steps without positional predicates
      final Step step = axisStep(s);
      if (step == null || !step.axis.down || step.has(Flag.FCS)) break;

      // check if path is iterable (i.e., will be duplicate-free)
      final boolean iter = pathNodes(data, s) != null;
      final IndexContext ictx = new IndexContext(data, iter);

      // choose cheapest index access
      final int pl = step.preds.length;
      for (int p = 0; p < pl; p++) {
        final IndexInfo ii = new IndexInfo(ictx, qc, step);
        if (!step.preds[p].indexAccessible(ii)) continue;

        if (ii.costs == 0) {
          // no results...
          qc.compInfo(OPTNOINDEX, this);
          return Empty.SEQ;
        }
        if (index == null || index.costs > ii.costs) {
          index = ii;
          iPred = p;
          iStep = s;
        }
      }
    }

    // skip rewriting if no index access is possible, or if it is too expensive
    if (index == null || index.costs > data.meta.size) return this;

    // rewrite for index access
    qc.compInfo(index.info);

    // replace expressions for index access
    final Step indexStep = index.step;

    // collect remaining predicates
    final int pl = indexStep.preds.length;
    final ExprList newPreds = new ExprList(pl - 1);
    for (int p = 0; p < pl; p++) {
      if (p != iPred) newPreds.add(indexStep.preds[p]);
    }

    // invert steps that occur before index step and add them as predicate
    final Test test = InvDocTest.get(rt);
    final ExprList invSteps = new ExprList();
    if (test != Test.DOC || !data.meta.uptodate || predSteps(data, iStep)) {
      for (int s = iStep; s >= 0; s--) {
        final Axis ax = axisStep(s).axis.invert();
        if (s == 0) {
          // add document test for collections and axes other than ancestors
          if (test != Test.DOC || ax != Axis.ANC && ax != Axis.ANCORSELF)
            invSteps.add(Step.get(info, ax, test));
        } else {
          final Step prev = axisStep(s - 1);
          invSteps.add(Step.get(info, ax, prev.test, prev.preds));
        }
      }
    }
    if (!invSteps.isEmpty()) newPreds.add(get(info, null, invSteps.finish()));

    // create resulting expression
    final ExprList resultSteps = new ExprList();
    final Expr resultRoot;
    if (index.expr instanceof Path) {
      final Path p = (Path) index.expr;
      resultRoot = p.root;
      resultSteps.add(p.steps);
    } else {
      resultRoot = index.expr;
    }

    if (!newPreds.isEmpty()) {
      int ls = resultSteps.size() - 1;
      Step step;
      if (ls < 0 || !(resultSteps.get(ls) instanceof Step)) {
        // add at least one self axis step
        step = Step.get(info, Axis.SELF, Test.NOD);
        ls++;
      } else {
        step = (Step) resultSteps.get(ls);
      }
      // add remaining predicates to last step
      resultSteps.set(ls, step.addPreds(newPreds.finish()));
    }

    // add remaining steps
    for (int s = iStep + 1; s < sl; s++) resultSteps.add(steps[s]);
    return get(info, resultRoot, resultSteps.finish());
  }