/** * Creates query plans. * * @param comp compiled flag */ private void plan(final boolean comp) { if (comp != options.get(MainOptions.COMPPLAN)) return; // show dot plan try { if (options.get(MainOptions.DOTPLAN)) { final String path = options.get(MainOptions.QUERYPATH); final String dot = path.isEmpty() ? "plan.dot" : new IOFile(path).name().replaceAll("\\..*?$", ".dot"); try (final BufferOutput bo = new BufferOutput(dot)) { try (final DOTSerializer d = new DOTSerializer(bo, options.get(MainOptions.DOTCOMPACT))) { d.serialize(qp.plan()); } } } // show XML plan if (options.get(MainOptions.XMLPLAN)) { info(NL + QUERY_PLAN + COL); info(qp.plan().serialize().toString()); } } catch (final Exception ex) { Util.stack(ex); } }
/** * Test query for stripping existing namespaces. * * @throws Exception exception */ @Test public void stripNS() throws Exception { final IO io = IO.get("<a xmlns:a='a'><b><c/><c/><c/></b></a>"); try (final QueryProcessor qp = new QueryProcessor("/*:a/*:b", context).context(new DBNode(io))) { final ANode sub = (ANode) qp.iter().next(); DataBuilder.stripNS(sub, token("a"), context); } }
/** * Parses the query. * * @param p performance * @throws QueryException query exception */ private void parse(final Performance p) throws QueryException { qp.http(http); for (final Entry<String, String[]> entry : vars.entrySet()) { final String name = entry.getKey(); final String[] value = entry.getValue(); if (name == null) qp.context(value[0], value[1]); else qp.bind(name, value[0], value[1]); } qp.parse(); if (p != null) info.parsing += p.time(); }
/** * Returns an extended error message. * * @param err error message * @return result of check */ private boolean extError(final String err) { // will only be evaluated when an error has occurred final StringBuilder sb = new StringBuilder(); if (options.get(MainOptions.QUERYINFO)) { sb.append(info()).append(qp.info()).append(NL).append(ERROR).append(COL).append(NL); } sb.append(err); return error(sb.toString()); }
/** Test query. Detects malformed namespace hierarchy. */ @Test @Ignore public void xuty0004() { final String query = "declare variable $input-context external;" + "let $source as node()* := (" + " <status>on leave</status>," + " <!-- for 6 months -->" + " )," + " $target := $input-context/works[1]/employee[1]" + "return insert nodes $source into $target"; try (final QueryProcessor qp = new QueryProcessor(query, context)) { qp.value(); } catch (final QueryException ex) { assertEquals("XUTY0004", ex.error().code); } fail("should throw XUTY0004"); }
@Override public void databases(final LockResult lr) { if (qp == null) { lr.writeAll = true; } else { qp.databases(lr); info.readLocked = lr.readAll ? null : lr.read; info.writeLocked = lr.writeAll ? null : lr.write; } }
/** * Parses a module. * * @param io input reference * @return query parser * @throws QueryException query exception */ final QueryParser parseQuery(final IO io) throws QueryException { try (final QueryContext qctx = new QueryContext(qc)) { final String input = string(io.read()); // parse query final QueryParser qp = new QueryParser(input, io.path(), qctx, null); module = QueryProcessor.isLibrary(input) ? qp.parseLibrary(true) : qp.parseMain(); return qp; } catch (final IOException | QueryException ex) { throw IOERR_X.get(info, ex); } }
/** * Checks if the query possibly performs updates. * * @param ctx database context * @param query query string * @return result of check */ final boolean updates(final Context ctx, final String query) { try { final Performance p = new Performance(); qp(query, ctx); parse(p); return qp.updating; } catch (final QueryException ex) { Util.debug(ex); exception = ex; qp.close(); return false; } }
/** * Evaluates the specified query. * * @param query query * @return success flag */ final boolean query(final String query) { final Performance p = new Performance(); String error; if (exception != null) { error = Util.message(exception); } else { try { long hits = 0; final boolean run = options.get(MainOptions.RUNQUERY); final boolean serial = options.get(MainOptions.SERIALIZE); final int runs = Math.max(1, options.get(MainOptions.RUNS)); for (int r = 0; r < runs; ++r) { // reuse existing processor instance if (r != 0) qp = null; qp(query, context); parse(p); if (r == 0) plan(false); qp.compile(); info.compiling += p.time(); if (r == 0) plan(true); if (!run) continue; final PrintOutput po = r == 0 && serial ? out : new NullOutput(); try (final Serializer ser = qp.getSerializer(po)) { if (maxResults >= 0) { result = qp.cache(maxResults); info.evaluating += p.time(); result.serialize(ser); hits = result.size(); } else { hits = 0; final Iter ir = qp.iter(); info.evaluating += p.time(); for (Item it; (it = ir.next()) != null; ) { ser.serialize(it); ++hits; checkStop(); } } } qp.close(); info.serializing += p.time(); } // dump some query info // out.flush(); // remove string list if global locking is used and if query is updating if (soptions.get(StaticOptions.GLOBALLOCK) && qp.updating) { info.readLocked = null; info.writeLocked = null; } return info(info.toString(qp, out.size(), hits, options.get(MainOptions.QUERYINFO))); } catch (final QueryException | IOException ex) { exception = ex; error = Util.message(ex); } catch (final ProcException ex) { error = INTERRUPTED; } catch (final StackOverflowError ex) { Util.debug(ex); error = BASX_STACKOVERFLOW.desc; } catch (final RuntimeException ex) { extError(""); Util.debug(info()); throw ex; } finally { // close processor after exceptions if (qp != null) qp.close(); } } return extError(error); }
@Override public final boolean updated(final Context ctx) { return qp != null && qp.updates() != 0; }
/** Closes the query processor. */ protected void closeQp() { if (qp != null) { qp.close(); qp = null; } }