Пример #1
0
  /**
   * Computes the number of results.
   *
   * @param qc query context (may be @code null)
   * @return number of results
   */
  private long size(final QueryContext qc) {
    final Value rt = initial(qc);
    // skip computation if value is not a document node
    if (rt == null || rt.type != NodeType.DOC) return -1;
    final Data data = rt.data();
    // skip computation if no database instance is available, is out-of-date or
    // if context does not contain all database nodes
    if (data == null || !data.meta.uptodate || data.resources.docs().size() != rt.size()) return -1;

    ArrayList<PathNode> nodes = data.paths.root();
    long m = 1;
    final int sl = steps.length;
    for (int s = 0; s < sl; s++) {
      final Step curr = axisStep(s);
      if (curr != null) {
        nodes = curr.nodes(nodes, data);
        if (nodes == null) return -1;
      } else if (s + 1 == sl) {
        m = steps[s].size();
      } else {
        // stop if a non-axis step is not placed last
        return -1;
      }
    }

    long sz = 0;
    for (final PathNode pn : nodes) sz += pn.stats.count;
    return sz * m;
  }
Пример #2
0
 /**
  * Checks if the expressions is a simple child step.
  *
  * @param expr expression to be checked
  * @return result of check
  */
 private boolean simpleChild(final Expr expr) {
   if (expr instanceof Step) {
     final Step n = (Step) expr;
     if (n.axis == CHILD && !n.has(Flag.FCS)) return true;
   }
   return false;
 }
Пример #3
0
  @Override
  public final Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException {
    final Value v = initial(qc);
    if (v != null && v.isEmpty() || emptyPath(v)) return optPre(qc);

    // merge descendant steps
    Expr e = mergeSteps(qc);
    if (e == this && v != null && v.type == NodeType.DOC) {
      // check index access
      e = index(qc, v);
      // rewrite descendant to child steps
      if (e == this) e = children(qc, v);
    }
    // recompile path if it has changed
    if (e != this) return e.compile(qc, scp);

    // set atomic type for single attribute steps to speed up predicate tests
    if (root == null && steps.length == 1 && steps[0] instanceof Step) {
      final Step curr = (Step) steps[0];
      if (curr.axis == ATTR && curr.test.kind == Kind.URI_NAME) curr.seqType(SeqType.NOD_ZO);
    }

    // choose best path implementation and set type information
    final Path path = get(info, root, steps);
    path.size = path.size(qc);
    path.seqType = SeqType.get(steps[steps.length - 1].seqType().type, size);
    return path;
  }
Пример #4
0
  /**
   * Converts descendant to child steps.
   *
   * @param qc query context
   * @param rt root value
   * @return original or new expression
   */
  private Expr children(final QueryContext qc, final Value rt) {
    // skip if index does not exist or is out-dated, or if several namespaces occur in the input
    final Data data = rt.data();
    if (data == null || !data.meta.uptodate || data.nspaces.globalNS() == null) return this;

    Path path = this;
    final int sl = steps.length;
    for (int s = 0; s < sl; s++) {
      // don't allow predicates in preceding location steps
      final Step prev = s > 0 ? axisStep(s - 1) : null;
      if (prev != null && prev.preds.length != 0) break;

      // ignore axes other than descendant, or numeric predicates
      final Step curr = axisStep(s);
      if (curr == null || curr.axis != DESC || curr.has(Flag.FCS)) continue;

      // check if child steps can be retrieved for current step
      ArrayList<PathNode> nodes = pathNodes(data, s);
      if (nodes == null) continue;

      // cache child steps
      final ArrayList<QNm> qnm = new ArrayList<>();
      while (nodes.get(0).parent != null) {
        QNm nm = new QNm(data.elemNames.key(nodes.get(0).name));
        // skip children with prefixes
        if (nm.hasPrefix()) return this;
        for (final PathNode p : nodes) {
          if (nodes.get(0).name != p.name) nm = null;
        }
        qnm.add(nm);
        nodes = PathSummary.parent(nodes);
      }
      qc.compInfo(OPTCHILD, steps[s]);

      // build new steps
      int ts = qnm.size();
      final Expr[] stps = new Expr[ts + sl - s - 1];
      for (int t = 0; t < ts; t++) {
        final Expr[] preds = t == ts - 1 ? ((Preds) steps[s]).preds : new Expr[0];
        final QNm nm = qnm.get(ts - t - 1);
        final NameTest nt =
            nm == null ? new NameTest(false) : new NameTest(nm, Kind.NAME, false, null);
        stps[t] = Step.get(info, CHILD, nt, preds);
      }
      while (++s < sl) stps[ts++] = steps[s];
      path = get(info, root, stps);
      break;
    }

    // check if all steps yield results; if not, return empty sequence
    final ArrayList<PathNode> nodes = pathNodes(qc);
    if (nodes != null && nodes.isEmpty()) {
      qc.compInfo(OPTPATH, path);
      return Empty.SEQ;
    }

    return path;
  }
Пример #5
0
  /**
   * Optimizes descendant-or-self steps and static types.
   *
   * @param ctx query context
   */
  void optSteps(final QueryContext ctx) {
    boolean opt = false;
    Expr[] st = steps;
    for (int l = 1; l < st.length; ++l) {
      if (!(st[l - 1] instanceof Step && st[l] instanceof Step)) continue;

      final Step prev = (Step) st[l - 1];
      final Step curr = (Step) st[l];
      if (!prev.simple(DESCORSELF, false)) continue;

      if (curr.axis == CHILD && !curr.has(Flag.FCS)) {
        // descendant-or-self::node()/child::X -> descendant::X
        final int sl = st.length;
        final Expr[] tmp = new Expr[sl - 1];
        System.arraycopy(st, 0, tmp, 0, l - 1);
        System.arraycopy(st, l, tmp, l - 1, sl - l);
        st = tmp;
        curr.axis = DESC;
        opt = true;
      } else if (curr.axis == ATTR && !curr.has(Flag.FCS)) {
        // descendant-or-self::node()/@X -> descendant-or-self::*/@X
        prev.test = new NameTest(false);
        opt = true;
      }
    }
    if (opt) ctx.compInfo(OPTDESC);

    // set atomic type for single attribute steps to speedup predicate tests
    if (root == null && st.length == 1 && st[0] instanceof Step) {
      final Step curr = (Step) st[0];
      if (curr.axis == ATTR && curr.test.mode == Mode.STD) curr.type = SeqType.NOD_ZO;
    }
    steps = st;
  }
Пример #6
0
  /**
   * Returns the path nodes that will result from this path.
   *
   * @param qc query context
   * @return path nodes, or {@code null} if nodes cannot be evaluated
   */
  public final ArrayList<PathNode> pathNodes(final QueryContext qc) {
    final Value init = initial(qc);
    final Data data = init != null && init.type == NodeType.DOC ? init.data() : null;
    if (data == null || !data.meta.uptodate) return null;

    ArrayList<PathNode> nodes = data.paths.root();
    final int sl = steps.length;
    for (int s = 0; s < sl; s++) {
      final Step curr = axisStep(s);
      if (curr == null) return null;
      nodes = curr.nodes(nodes, data);
      if (nodes == null) return null;
    }
    return nodes;
  }
Пример #7
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;
  }
Пример #8
0
 /**
  * Returns a new path instance.
  *
  * @param ii input info
  * @param r root expression; can be a {@code null} reference
  * @param path path steps
  * @return class instance
  */
 public static Path get(final InputInfo ii, final Expr r, final Expr... path) {
   // check if all steps are axis steps
   boolean axes = true;
   for (int p = 0; p < path.length; p++) {
     Expr e = path[p];
     if (e instanceof Context) {
       e = Step.get(((ParseExpr) e).info, SELF, Test.NOD);
     } else if (e instanceof Filter) {
       final Filter f = (Filter) e;
       if (f.root instanceof Context) {
         e = Step.get(f.info, SELF, Test.NOD, f.preds);
       }
     }
     axes &= e instanceof Step;
     path[p] = e;
   }
   return axes ? new CachedPath(ii, r, path).finish(null) : new MixedPath(ii, r, path);
 }
Пример #9
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);
  }
Пример #10
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());
  }
Пример #11
0
  /**
   * Converts descendant to child steps.
   *
   * @param ctx query context
   * @param data data reference
   * @return path
   */
  Expr children(final QueryContext ctx, final Data data) {
    // skip path check if no path index exists, or if it is out-of-date
    if (!data.meta.uptodate || data.nspaces.globalNS() == null) return this;

    Path path = this;
    for (int s = 0; s < steps.length; ++s) {
      // don't allow predicates in preceding location steps
      final Step prev = s > 0 ? axisStep(s - 1) : null;
      if (prev != null && prev.preds.length != 0) break;

      // ignore axes other than descendant, or numeric predicates
      final Step curr = axisStep(s);
      if (curr == null || curr.axis != DESC || curr.has(Flag.FCS)) continue;

      // check if child steps can be retrieved for current step
      ArrayList<PathNode> pn = pathNodes(data, s);
      if (pn == null) continue;

      // cache child steps
      final ArrayList<QNm> qnm = new ArrayList<>();
      while (pn.get(0).par != null) {
        QNm nm = new QNm(data.tagindex.key(pn.get(0).name));
        // skip children with prefixes
        if (nm.hasPrefix()) return this;
        for (final PathNode p : pn) {
          if (pn.get(0).name != p.name) nm = null;
        }
        qnm.add(nm);
        pn = PathSummary.parent(pn);
      }
      ctx.compInfo(OPTCHILD, steps[s]);

      // build new steps
      int ts = qnm.size();
      final Expr[] stps = new Expr[ts + steps.length - s - 1];
      for (int t = 0; t < ts; ++t) {
        final Expr[] preds = t == ts - 1 ? ((Preds) steps[s]).preds : new Expr[0];
        final QNm nm = qnm.get(ts - t - 1);
        final NameTest nt =
            nm == null ? new NameTest(false) : new NameTest(nm, Mode.LN, false, null);
        stps[t] = Step.get(info, CHILD, nt, preds);
      }
      while (++s < steps.length) stps[ts++] = steps[s];
      path = get(info, root, stps);
      break;
    }

    // check if the all children in the path exist; don't test with namespaces
    if (data.nspaces.size() == 0) {
      LOOP:
      for (int s = 0; s < path.steps.length; ++s) {
        // only verify child steps; ignore namespaces
        final Step st = path.axisStep(s);
        if (st == null || st.axis != CHILD) break;
        if (st.test.mode == Mode.ALL || st.test.mode == null) continue;
        if (st.test.mode != Mode.LN) break;

        // check if one of the addressed nodes is on the correct level
        final int name = data.tagindex.id(st.test.name.local());
        for (final PathNode pn : data.paths.desc(name, Data.ELEM)) {
          if (pn.level() == s + 1) continue LOOP;
        }
        ctx.compInfo(OPTPATH, path);
        return Empty.SEQ;
      }
    }
    return path;
  }