Пример #1
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;
  }
Пример #2
0
  /**
   * If possible, returns an expression which accesses the index. Otherwise, returns the original
   * expression.
   *
   * @param ctx query context
   * @param data data reference
   * @return resulting expression
   * @throws QueryException query exception
   */
  private Expr index(final QueryContext ctx, final Data data) throws QueryException {

    // disallow relative paths and numeric predicates
    if (root == null || uses(Use.POS)) return this;

    // cache index access costs
    IndexContext ics = null;
    // cheapest predicate and step
    int pmin = 0;
    int smin = 0;

    // check if path can be converted to an index access
    for (int s = 0; s < steps.length; ++s) {
      // find cheapest index access
      final AxisStep stp = step(s);
      if (!stp.axis.down) break;

      // check if resulting index path will be duplicate free
      final boolean i = pathNodes(data, s) != null;

      // choose cheapest index access
      for (int p = 0; p < stp.preds.length; ++p) {
        final IndexContext ic = new IndexContext(ctx, data, stp, i);
        if (!stp.preds[p].indexAccessible(ic)) continue;

        if (ic.costs() == 0) {
          if (ic.not) {
            // not operator... accept all results
            stp.preds[p] = Bln.TRUE;
            continue;
          }
          // no results...
          ctx.compInfo(OPTNOINDEX, this);
          return Empty.SEQ;
        }
        if (ics == null || ics.costs() > ic.costs()) {
          ics = ic;
          pmin = p;
          smin = s;
        }
      }
    }

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

    // replace expressions for index access
    final AxisStep stp = step(smin);
    final Expr ie = stp.preds[pmin].indexEquivalent(ics);

    if (ics.seq) {
      // sequential evaluation; do not invert path
      stp.preds[pmin] = ie;
    } else {
      // inverted path, which will be represented as predicate
      AxisStep[] invSteps = {};

      // collect remaining predicates
      final Expr[] newPreds = new Expr[stp.preds.length - 1];
      int c = 0;
      for (int p = 0; p != stp.preds.length; ++p) {
        if (p != pmin) newPreds[c++] = stp.preds[p];
      }

      // check if path before index step needs to be inverted and traversed
      final Test test = DocTest.get(ctx, data);
      boolean inv = true;
      if (test == Test.DOC && data.meta.pathindex && data.meta.uptodate) {
        int j = 0;
        for (; j <= smin; ++j) {
          // invert if axis is not a child or has predicates
          final AxisStep s = axisStep(j);
          if (s == null) break;
          if (s.axis != Axis.CHILD || s.preds.length > 0 && j != smin) break;
          if (s.test.test == Name.ALL || s.test.test == null) continue;
          if (s.test.test != Name.NAME) break;

          // support only unique paths with nodes on the correct level
          final int name = data.tagindex.id(s.test.name.local());
          final ObjList<PathNode> pn = data.paths.desc(name, Data.ELEM);
          if (pn.size() != 1 || pn.get(0).level() != j + 1) break;
        }
        inv = j <= smin;
      }

      // invert path before index step
      if (inv) {
        for (int j = smin; j >= 0; --j) {
          final Axis ax = step(j).axis.invert();
          if (ax == null) break;
          if (j != 0) {
            final AxisStep prev = step(j - 1);
            invSteps = Array.add(invSteps, AxisStep.get(input, ax, prev.test, prev.preds));
          } else {
            // add document test for collections and axes other than ancestors
            if (test != Test.DOC || ax != Axis.ANC && ax != Axis.ANCORSELF)
              invSteps = Array.add(invSteps, AxisStep.get(input, ax, test));
          }
        }
      }

      // create resulting expression
      final AxisPath result;
      final boolean simple = invSteps.length == 0 && newPreds.length == 0;
      if (ie instanceof AxisPath) {
        result = (AxisPath) ie;
      } else if (smin + 1 < steps.length || !simple) {
        result =
            simple
                ? new AxisPath(input, ie)
                : new AxisPath(input, ie, AxisStep.get(input, Axis.SELF, Test.NOD));
      } else {
        return ie;
      }

      // add remaining predicates to last step
      final int ls = result.steps.length - 1;
      if (ls >= 0) {
        result.steps[ls] = result.step(ls).addPreds(newPreds);
        // add inverted path as predicate to last step
        if (invSteps.length != 0)
          result.steps[ls] = result.step(ls).addPreds(Path.get(input, null, invSteps));
      }

      // add remaining steps
      for (int s = smin + 1; s < steps.length; ++s) {
        result.steps = Array.add(result.steps, steps[s]);
      }
      return result;
    }
    return this;
  }
Пример #3
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;
  }