Example #1
0
  @Override
  public Value value(final QueryContext ctx) throws QueryException {
    final int o = (int) ctx.resources.output.size();
    final Updates updates = ctx.resources.updates();
    final ContextModifier tmp = updates.mod;
    final TransformModifier pu = new TransformModifier();
    updates.mod = pu;

    try {
      for (final Let fo : copies) {
        final Iter ir = ctx.iter(fo.expr);
        Item i = ir.next();
        if (!(i instanceof ANode) || ir.next() != null) throw UPCOPYMULT.get(fo.info, fo.var.name);

        // copy node to main memory data instance
        i = ((ANode) i).dbCopy(ctx.context.options);
        // add resulting node to variable
        ctx.set(fo.var, i, info);
        pu.addData(i.data());
      }
      final Value v = ctx.value(expr[0]);
      if (!v.isEmpty()) throw BASEX_MOD.get(info);

      updates.prepare();
      updates.apply();
      return ctx.value(expr[1]);
    } finally {
      ctx.resources.output.size(o);
      updates.mod = tmp;
    }
  }
Example #2
0
  @Override
  public final Expr compile(final QueryContext qc, final VarScope scp) throws QueryException {
    if (root != null) root = root.compile(qc, scp);
    // no steps
    if (steps.length == 0) return root == null ? new Context(info) : root;

    final Value init = qc.value, cv = initial(qc);
    final boolean doc = cv != null && cv.type == NodeType.DOC;
    qc.value = cv;
    try {
      final int sl = steps.length;
      for (int s = 0; s < sl; s++) {
        Expr e = steps[s];

        // axis step: if input is a document, its type is temporarily generalized
        final boolean as = e instanceof Step;
        if (as && s == 0 && doc) cv.type = NodeType.NOD;

        e = e.compile(qc, scp);
        if (e.isEmpty()) return optPre(qc);
        steps[s] = e;

        // no axis step: invalidate context value
        if (!as) qc.value = null;
      }
    } finally {
      if (doc) cv.type = NodeType.DOC;
      qc.value = init;
    }
    // optimize path
    return optimize(qc, scp);
  }
Example #3
0
  @Override
  public final Expr inline(final QueryContext ctx, final VarScope scp, final Var v, final Expr e)
      throws QueryException {

    final Value oldVal = ctx.value;
    try {
      ctx.value = root(ctx);
      final Expr rt = root == null ? null : root.inline(ctx, scp, v, e);
      if (rt != null) {
        setRoot(ctx, rt);
        ctx.value = oldVal;
        ctx.value = root(ctx);
      }

      boolean change = rt != null;
      for (int i = 0; i < steps.length; i++) {
        final Expr nw = steps[i].inline(ctx, scp, v, e);
        if (nw != null) {
          steps[i] = nw;
          change = true;
        }
      }
      return change ? optimize(ctx, scp) : null;
    } finally {
      ctx.value = oldVal;
    }
  }
Example #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;
  }
Example #5
0
  @Override
  public final Expr compile(final QueryContext ctx, final VarScope scp) throws QueryException {
    if (root != null) setRoot(ctx, root.compile(ctx, scp));

    final Value v = ctx.value;
    try {
      ctx.value = root(ctx);
      return compilePath(ctx, scp);
    } finally {
      ctx.value = v;
    }
  }
Example #6
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;
  }
Example #7
0
 /**
  * Sets a new root expression and eliminates a superfluous context item.
  *
  * @param ctx query context
  * @param rt root expression
  */
 private void setRoot(final QueryContext ctx, final Expr rt) {
   root = rt;
   if (root instanceof Context) {
     ctx.compInfo(OPTREMCTX);
     root = null;
   }
 }
Example #8
0
  /**
   * Extracts entries from the archive.
   *
   * @param ctx query context
   * @return text entries
   * @throws QueryException query exception
   */
  private TokenList extract(final QueryContext ctx) throws QueryException {
    final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64);
    TokenSet hs = null;
    if (expr.length > 1) {
      // filter result to specified entries
      hs = new TokenSet();
      final Iter names = ctx.iter(expr[1]);
      for (Item en; (en = names.next()) != null; ) {
        hs.add(checkElmStr(en).string(info));
      }
    }

    final TokenList tl = new TokenList();
    final ArchiveIn in = ArchiveIn.get(archive.input(info), info);
    try {
      while (in.more()) {
        final ZipEntry ze = in.entry();
        if (ze.isDirectory()) continue;
        if (hs == null || hs.delete(token(ze.getName())) != 0) tl.add(in.read());
      }
    } catch (final IOException ex) {
      Util.debug(ex);
      ARCH_FAIL.thrw(info, ex);
    } finally {
      in.close();
    }
    return tl;
  }
Example #9
0
 @Override
 public final Value value(final QueryContext qc) throws QueryException {
   final int es = exprs.length;
   final Value[] args = new Value[es];
   for (int e = 0; e < es; ++e) args[e] = qc.value(exprs[e]);
   return toValue(eval(args, qc), qc, sc);
 }
Example #10
0
  /**
   * Returns the specified function literal.
   *
   * @param name function name
   * @param arity number of arguments
   * @param dyn dynamic invocation flag
   * @param ctx query context
   * @param ii input info
   * @return literal function expression
   * @throws QueryException query exception
   */
  public static FItem get(
      final QNm name,
      final long arity,
      final boolean dyn,
      final QueryContext ctx,
      final InputInfo ii)
      throws QueryException {

    final Expr[] args = new Expr[(int) arity];
    final Var[] vars = new Var[args.length];
    for (int i = 0; i < args.length; i++) {
      vars[i] = ctx.uniqueVar(ii, null);
      args[i] = new VarRef(ii, vars[i]);
    }

    final TypedFunc f = get(name, args, dyn, ctx, ii);
    if (f == null) {
      if (!dyn) FUNCUNKNOWN.thrw(ii, name + "#" + arity);
      return null;
    }

    // compile the function if it hasn't been done statically
    if (dyn && f.fun instanceof UserFuncCall) {
      final UserFunc usf = ((UserFuncCall) f.fun).func();
      if (usf != null && usf.declared) usf.compile(ctx);
    }

    final FuncType ft = f.type;
    return new FuncItem(name, vars, f.fun, ft, false);
  }
Example #11
0
 /**
  * Performs the test-uris function.
  *
  * @param ctx query context
  * @return resulting value
  * @throws QueryException query exception
  */
 private Item testUris(final QueryContext ctx) throws QueryException {
   checkCreate(ctx);
   final ArrayList<IO> inputs = new ArrayList<>();
   final Iter ir = ctx.iter(expr[0]);
   for (Item it; (it = ir.next()) != null; ) inputs.add(checkPath(it, ctx));
   return new Suite(ctx, info).test(inputs);
 }
Example #12
0
 @Override
 Value[] evalArgs(final QueryContext ctx) throws QueryException {
   final int al = expr.length - 1;
   final Value[] args = new Value[al];
   for (int a = 0; a < al; ++a) args[a] = ctx.value(expr[a]);
   return args;
 }
Example #13
0
 /**
  * Performs the test-libraries function (still experimental).
  *
  * @param ctx query context
  * @return resulting value
  * @throws QueryException query exception
  */
 private Item testLibraries(final QueryContext ctx) throws QueryException {
   checkCreate(ctx);
   final TokenList tl = new TokenList();
   final Iter ir = ctx.iter(expr[0]);
   for (Item it; (it = ir.next()) != null; ) tl.add(checkStr(it));
   return new Suite(ctx, info).test(tl);
 }
Example #14
0
 /**
  * Returns a serializer for the given output stream. Optional output declarations within the query
  * will be included in the serializer instance.
  *
  * @param os output stream
  * @return serializer instance
  * @throws IOException query exception
  * @throws QueryException query exception
  */
 public Serializer getSerializer(final OutputStream os) throws IOException, QueryException {
   compile();
   try {
     return Serializer.get(os, qc.serParams());
   } catch (final QueryIOException ex) {
     throw ex.getCause();
   }
 }
Example #15
0
 /**
  * Performs the assert-equals function.
  *
  * @param ctx query context
  * @return resulting value
  * @throws QueryException query exception
  */
 private Item assertEquals(final QueryContext ctx) throws QueryException {
   final byte[] str = expr.length < 3 ? null : checkStr(expr[2], ctx);
   final Iter iter1 = ctx.iter(expr[0]), iter2 = ctx.iter(expr[1]);
   final Compare comp = new Compare(info);
   Item it1, it2;
   int c = 1;
   while (true) {
     it1 = iter1.next();
     it2 = iter2.next();
     final boolean empty1 = it1 == null, empty2 = it2 == null;
     if (empty1 && empty2) return null;
     if (empty1 || empty2 || !comp.deep(it1.iter(), it2.iter())) break;
     c++;
   }
   if (str != null) throw UNIT_MESSAGE.get(info, str);
   throw new UnitException(info, UNIT_ASSERT_EQUALS, it1, it2, c);
 }
Example #16
0
 /**
  * Returns all tokens of the query.
  *
  * @param qc query context
  * @return token list
  * @throws QueryException query exception
  */
 private TokenList tokens(final QueryContext qc) throws QueryException {
   final TokenList tl = new TokenList();
   final Iter ir = qc.iter(query);
   for (byte[] qu; (qu = nextToken(ir)) != null; ) {
     // skip empty tokens if not all results are needed
     if (qu.length != 0 || mode == FTMode.ALL || mode == FTMode.ALL_WORDS) tl.add(qu);
   }
   return tl;
 }
Example #17
0
 /**
  * Parses the query.
  *
  * @throws QueryException query exception
  */
 public void parse() throws QueryException {
   if (parsed) return;
   try {
     qc.parseMain(query, null, sc);
   } finally {
     parsed = true;
     updating = qc.updating;
   }
 }
Example #18
0
  @Override
  public Expr optimize(final QueryContext ctx, final VarScope scp) throws QueryException {
    if (root instanceof Context) {
      ctx.compInfo(OPTREMCTX);
      root = null;
    }

    for (final Expr e : steps) {
      // check for empty steps
      if (e.isEmpty()) return optPre(null, ctx);
    }

    return this;
  }
Example #19
0
  /**
   * Performs the test function.
   *
   * @param ctx query context
   * @return resulting value
   * @throws QueryException query exception
   */
  private Item test(final QueryContext ctx) throws QueryException {
    final Unit unit = new Unit(ctx, info);
    if (expr.length == 0) return unit.test(sc);

    final ArrayList<StaticFunc> funcs = new ArrayList<>();
    final Iter ir = ctx.iter(expr[0]);
    for (Item it; (it = ir.next()) != null; ) {
      final FItem fi = checkFunc(it, ctx);
      if (fi.funcName() != null) {
        final StaticFunc sf = ctx.funcs.get(fi.funcName(), fi.arity(), null, true);
        if (sf != null) funcs.add(sf);
      }
    }
    return unit.test(sc, funcs);
  }
Example #20
0
  /**
   * Creates a new archive.
   *
   * @param ctx query context
   * @return archive
   * @throws QueryException query exception
   */
  private B64 create(final QueryContext ctx) throws QueryException {
    final Iter entr = ctx.iter(expr[0]);
    final Iter cont = ctx.iter(expr[1]);
    final Item opt = expr.length > 2 ? expr[2].item(ctx, info) : null;
    final TokenMap map = new FuncParams(Q_OPTIONS, info).parse(opt);

    final byte[] f = map.get(FORMAT);
    final String format = f != null ? string(lc(f)) : "zip";
    final ArchiveOut out = ArchiveOut.get(format, info);
    // check algorithm
    final byte[] alg = map.get(ALGORITHM);
    int level = ZipEntry.DEFLATED;
    if (alg != null) {
      if (format.equals("zip") && !eq(alg, STORED, DEFLATE)
          || format.equals("gzip") && !eq(alg, DEFLATE)) {
        ARCH_SUPP.thrw(info, ALGORITHM, alg);
      }
      if (eq(alg, STORED)) level = ZipEntry.STORED;
      else if (eq(alg, DEFLATE)) level = ZipEntry.DEFLATED;
    }
    out.level(level);

    try {
      int e = 0;
      int c = 0;
      Item en, cn;
      while (true) {
        en = entr.next();
        cn = cont.next();
        if (en == null || cn == null) break;
        if (out instanceof GZIPOut && c > 0)
          ARCH_ONE.thrw(info, format.toUpperCase(Locale.ENGLISH));
        add(checkElmStr(en), cn, out, level);
        e++;
        c++;
      }
      // count remaining entries
      if (cn != null) do c++; while (cont.next() != null);
      if (en != null) do e++; while (entr.next() != null);
      if (e != c) throw ARCH_DIFF.thrw(info, e, c);
    } catch (final IOException ex) {
      Util.debug(ex);
      throw ARCH_FAIL.thrw(info, ex);
    } finally {
      out.close();
    }
    return new B64(out.toArray());
  }
Example #21
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 #22
0
  /**
   * Updates an archive.
   *
   * @param ctx query context
   * @return updated archive
   * @throws QueryException query exception
   */
  private B64 update(final QueryContext ctx) throws QueryException {
    final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64);
    // entries to be updated
    final TokenObjMap<Item[]> hm = new TokenObjMap<Item[]>();

    final Iter entr = ctx.iter(expr[1]);
    final Iter cont = ctx.iter(expr[2]);
    int e = 0;
    int c = 0;
    Item en, cn;
    while (true) {
      en = entr.next();
      cn = cont.next();
      if (en == null || cn == null) break;
      hm.add(checkElmStr(en).string(info), new Item[] {en, cn});
      e++;
      c++;
    }
    // count remaining entries
    if (cn != null) do c++; while (cont.next() != null);
    if (en != null) do e++; while (entr.next() != null);
    if (e != c) ARCH_DIFF.thrw(info, e, c);

    final ArchiveIn in = ArchiveIn.get(archive.input(info), info);
    final ArchiveOut out = ArchiveOut.get(in.format(), info);
    try {
      if (in instanceof GZIPIn) ARCH_MODIFY.thrw(info, in.format().toUpperCase(Locale.ENGLISH));
      // delete entries to be updated
      while (in.more()) if (!hm.contains(token(in.entry().getName()))) out.write(in);
      // add new and updated entries
      for (final byte[] h : hm) {
        if (h == null) continue;
        final Item[] it = hm.get(h);
        add(it[0], it[1], out, ZipEntry.DEFLATED);
      }
    } catch (final IOException ex) {
      Util.debug(ex);
      ARCH_FAIL.thrw(info, ex);
    } finally {
      in.close();
      out.close();
    }
    return new B64(out.toArray());
  }
Example #23
0
  /**
   * Deletes files from an archive.
   *
   * @param ctx query context
   * @return updated archive
   * @throws QueryException query exception
   */
  private B64 delete(final QueryContext ctx) throws QueryException {
    final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64);
    // entries to be deleted
    final TokenObjMap<Item[]> hm = new TokenObjMap<Item[]>();
    final Iter names = ctx.iter(expr[1]);
    for (Item en; (en = names.next()) != null; ) {
      hm.add(checkElmStr(en).string(info), null);
    }

    final ArchiveIn in = ArchiveIn.get(archive.input(info), info);
    final ArchiveOut out = ArchiveOut.get(in.format(), info);
    try {
      if (in instanceof GZIPIn) ARCH_MODIFY.thrw(info, in.format().toUpperCase(Locale.ENGLISH));
      while (in.more()) if (!hm.contains(token(in.entry().getName()))) out.write(in);
    } catch (final IOException ex) {
      Util.debug(ex);
      ARCH_FAIL.thrw(info, ex);
    } finally {
      in.close();
      out.close();
    }
    return new B64(out.toArray());
  }
Example #24
0
 /**
  * Returns a result value.
  *
  * @return result value
  * @throws QueryException query exception
  */
 public Value value() throws QueryException {
   parse();
   return qc.iter().value();
 }
Example #25
0
  /**
   * Returns an instance of a with the specified name and number of arguments, or {@code null}.
   *
   * @param name name of the function
   * @param args optional arguments
   * @param dyn compile-/run-time flag
   * @param ctx query context
   * @param ii input info
   * @return function instance
   * @throws QueryException query exception
   */
  public static TypedFunc get(
      final QNm name,
      final Expr[] args,
      final boolean dyn,
      final QueryContext ctx,
      final InputInfo ii)
      throws QueryException {

    // get namespace and local name
    // parse data type constructors
    if (eq(name.uri(), XSURI)) {
      final byte[] ln = name.local();
      final AtomType type = AtomType.find(name, false);
      if (type == null) {
        final Levenshtein ls = new Levenshtein();
        for (final AtomType t : AtomType.values()) {
          if (t.par != null
              && t != AtomType.NOT
              && t != AtomType.AAT
              && t != AtomType.BIN
              && ls.similar(lc(ln), lc(t.string()), 0))
            FUNSIMILAR.thrw(ii, name.string(), t.string());
        }
      }
      // no constructor function found, or abstract type specified
      if (type == null || type == AtomType.NOT || type == AtomType.AAT) {
        FUNCUNKNOWN.thrw(ii, name.string());
      }

      if (args.length != 1) FUNCTYPE.thrw(ii, name.string());
      final SeqType to = SeqType.get(type, Occ.ZERO_ONE);
      return TypedFunc.constr(new Cast(ii, args[0], to), to);
    }

    // pre-defined functions
    final StandardFunc fun = Functions.get().get(name, args, ii);
    if (fun != null) {
      if (!ctx.sc.xquery3 && fun.xquery3()) FEATURE30.thrw(ii);
      for (final Function f : Function.UPDATING) {
        if (fun.sig == f) {
          ctx.updating(true);
          break;
        }
      }
      return new TypedFunc(fun, fun.sig.type(args.length));
    }

    // user-defined function
    final TypedFunc tf = ctx.funcs.get(name, args, ii);
    if (tf != null) return tf;

    // Java function (only allowed with administrator permissions)
    final JavaMapping jf = JavaMapping.get(name, args, ctx, ii);
    if (jf != null) return TypedFunc.java(jf);

    // add user-defined function that has not been declared yet
    if (!dyn && FuncType.find(name) == null) return ctx.funcs.add(name, args, ii, ctx);

    // no function found
    return null;
  }
Example #26
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());
  }
Example #27
0
 @Override
 public Value value(final QueryContext qc) throws QueryException {
   final Value val = qc.value(exprs[0]);
   if (val.isEmpty()) throw ONEORMORE.get(info);
   return val;
 }
Example #28
0
  /**
   * Evaluates the specified function and creates a response.
   *
   * @throws Exception exception (including unexpected ones)
   */
  void create() throws Exception {
    // bind variables
    final StaticFunc sf = function.function;
    final Expr[] args = new Expr[sf.args.length];
    function.bind(http, args, error);

    // wrap function with a function call
    final MainModule mm = new MainModule(sf, args);

    // assign main module and http context and register process
    query.mainModule(mm);
    query.http(http);
    query.context.register(query);

    String redirect = null, forward = null;
    RestXqRespBuilder resp = null;
    try {
      // compile and evaluate query
      query.compile();
      final Iter iter = query.iter();
      Item item = iter.next();

      // handle response element
      if (item != null && item instanceof ANode) {
        final ANode node = (ANode) item;
        // send redirect to browser
        if (REST_REDIRECT.eq(node)) {
          final ANode ch = node.children().next();
          if (ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name());
          redirect = string(ch.string()).trim();
          return;
        }
        // server-side forwarding
        if (REST_FORWARD.eq(node)) {
          final ANode ch = node.children().next();
          if (ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name());
          forward = string(ch.string()).trim();
          return;
        }
        if (REST_RESPONSE.eq(node)) {
          resp = new RestXqRespBuilder();
          resp.build(node, function, iter, http);
          return;
        }
      }

      // HEAD method must return a single response element
      if (function.methods.size() == 1 && function.methods.contains(HTTPMethod.HEAD.name()))
        throw function.error(HEAD_METHOD);

      // serialize result
      final SerializerOptions sp = function.output;
      http.sopts(sp);
      http.initResponse();
      final Serializer ser = Serializer.get(http.res.getOutputStream(), sp);
      for (; item != null; item = iter.next()) ser.serialize(item);
      ser.close();

    } finally {
      query.close();
      query.context.unregister(query);

      if (redirect != null) {
        http.res.sendRedirect(redirect);
      } else if (forward != null) {
        http.req.getRequestDispatcher(forward).forward(http.req, http.res);
      } else if (resp != null) {
        if (resp.status != 0) http.status(resp.status, resp.message, resp.error);
        http.res.getOutputStream().write(resp.cache.toArray());
      }
    }
  }
Example #29
0
 /**
  * Compiles the query.
  *
  * @throws QueryException query exception
  */
 public void compile() throws QueryException {
   parse();
   qc.compile();
 }
Example #30
0
 /**
  * Returns a result iterator.
  *
  * @return result iterator
  * @throws QueryException query exception
  */
 public Iter iter() throws QueryException {
   parse();
   return qc.iter();
 }