/** * 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 digest authentication. * * @throws Exception exception */ @Test public void digest() throws Exception { // correct credentials try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request xmlns:http='http://expath.org/ns/http-client' method='GET' " + "send-authorization='true' auth-method='Digest' username='******' password='******' " + "href='" + REST_ROOT + "'/>"), ctx)) { checkResponse(qp.value(), 2, HttpURLConnection.HTTP_OK); } // wrong credentials try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request xmlns:http='http://expath.org/ns/http-client' method='GET' " + "send-authorization='true' auth-method='Digest' username='******' password='******' " + "href='" + REST_ROOT + "?query=()'/>") + "[. instance of node()][@status = '401']", ctx)) { checkResponse(qp.value(), 1, HttpURLConnection.HTTP_UNAUTHORIZED); } }
/** * 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); } }
/** * Tests an erroneous query. * * @throws Exception exception */ @Test public void error() throws Exception { try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args("<http:request method='get'/>", RESTURL + "unknown") + "[1]/@status/data()", ctx)) { assertEquals("404", qp.value().serialize().toString()); } }
/** * 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(); }
/** * Test sending of HTTP PUT requests. * * @throws Exception exception */ @Test public void put() throws Exception { try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='put' status-only='true'>" + "<http:body media-type='text/xml'>" + BOOKS + "</http:body>" + "</http:request>", RESTURL), ctx)) { checkResponse(qp.value(), 1, HttpURLConnection.HTTP_CREATED); } }
/** * 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); } }
/** * Test sending of HTTP GET requests. * * @throws Exception exception */ @Test public void postGet() throws Exception { // GET1 - just send a GET request try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args("<http:request method='get' href='" + REST_ROOT + "'/>"), ctx)) { final Value v = qp.value(); checkResponse(v, 2, HttpURLConnection.HTTP_OK); assertEquals(NodeType.DOC, v.itemAt(1).type); } // GET2 - with override-media-type='text/plain' try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='get' override-media-type='text/plain'/>", REST_ROOT), ctx)) { final Value v = qp.value(); checkResponse(v, 2, HttpURLConnection.HTTP_OK); assertEquals(AtomType.STR, v.itemAt(1).type); } // Get3 - with status-only='true' try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args("<http:request method='get' status-only='true'/>", REST_ROOT), ctx)) { checkResponse(qp.value(), 1, HttpURLConnection.HTTP_OK); } }
/** * 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; } }
/** * Test sending of HTTP DELETE requests. * * @throws Exception exception */ @Test public void postDelete() throws Exception { // add document to be deleted try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='put'>" + "<http:body media-type='text/xml'><ToBeDeleted/></http:body>" + "</http:request>", RESTURL), ctx)) { qp.value(); } // DELETE try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args("<http:request method='delete' status-only='true'/>", RESTURL), ctx)) { checkResponse(qp.value(), 1, HttpURLConnection.HTTP_OK); } }
/** * Test sending of HTTP POST requests. * * @throws Exception exception */ @Test public void putPost() throws Exception { // PUT - query try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='put' status-only='true'>" + "<http:body media-type='text/xml'>" + BOOKS + "</http:body>" + "</http:request>", RESTURL), ctx)) { checkResponse(qp.value(), 1, HttpURLConnection.HTTP_CREATED); } // POST - query try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='post'>" + "<http:body media-type='application/xml'>" + "<query xmlns='" + Prop.URL + "/rest'>" + "<text><![CDATA[<x>1</x>]]></text>" + "</query>" + "</http:body>" + "</http:request>", RESTURL), ctx)) { checkResponse(qp.value(), 2, HttpURLConnection.HTTP_OK); } // Execute the same query but with content set from $bodies try (final QueryProcessor qp = new QueryProcessor( _HTTP_SEND_REQUEST.args( "<http:request method='post'>" + "<http:body media-type='application/xml'/>" + "</http:request>", RESTURL, "<query xmlns='" + Prop.URL + "/rest'>" + "<text><![CDATA[<x>1</x>]]></text>" + "</query>"), ctx)) { checkResponse(qp.value(), 2, HttpURLConnection.HTTP_OK); } }
/** * Refreshes the view after a file has been saved. * * @param root root directory * @param ctx database context * @throws InterruptedException interruption */ void parse(final IOFile root, final Context ctx) throws InterruptedException { final long id = ++parseId; final HashSet<String> parsed = new HashSet<>(); final TreeMap<String, InputInfo> errs = new TreeMap<>(); // collect files to be parsed final ProjectCache pc = cache(root); final StringList mods = new StringList(), lmods = new StringList(); for (final String path : pc) { final IOFile file = new IOFile(path); if (file.hasSuffix(IO.XQSUFFIXES)) (file.hasSuffix(IO.XQMSUFFIX) ? lmods : mods).add(path); } mods.add(lmods); // parse modules for (final String path : mods) { if (id != parseId) throw new InterruptedException(); if (parsed.contains(path)) continue; final IOFile file = new IOFile(path); try (final TextInput ti = new TextInput(file)) { // parse query try (final QueryContext qc = new QueryContext(ctx)) { final String input = ti.cache().toString(); final boolean lib = QueryProcessor.isLibrary(input); qc.parse(input, lib, path, null); // parsing was successful: remember path parsed.add(path); for (final byte[] mod : qc.modParsed) parsed.add(Token.string(mod)); } catch (final QueryException ex) { // parsing failed: remember path final InputInfo ii = ex.info(); errs.put(path, ii); parsed.add(ii.path()); } } catch (final IOException ex) { // file may not be accessible Util.debug(ex); } } errors = errs; }
/** Closes the query processor. */ protected void closeQp() { if (qp != null) { qp.close(); qp = null; } }
/** * 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; }