package org.basex.server; import static org.basex.query.QueryError.*; import java.io.*; import org.basex.api.client.*; import org.basex.core.*; import org.basex.core.jobs.*; import org.basex.io.out.*; import org.basex.io.serial.*; import org.basex.query.*; import org.basex.query.iter.*; import org.basex.query.value.item.*; import org.basex.util.*; /** * Server query representation, generated by {@link LocalQuery} and {@link ClientQuery}. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public final class ServerQuery extends Job { /** Query string. */ private final String query; /** Database context. */ private final Context ctx; /** Query processor. */ private QueryProcessor qp; /** Parsing flag. */ private boolean parsed; /** Query info. */ private String info = ""; /** * Constructor. * @param query query string * @param ctx database context */ public ServerQuery(final String query, final Context ctx) { this.query = query; this.ctx = ctx; } /** * Binds a global variable. * @param name name of variable * @param value value to be bound * @param type type * @throws IOException query exception */ public void bind(final String name, final Object value, final String type) throws IOException { try { qp().bind(name, value, type); } catch(final QueryException ex) { Util.stack(ex); throw new BaseXException(ex); } } /** * Binds the context value. * @param value value to be bound * @param type type * @throws IOException query exception */ public void context(final Object value, final String type) throws IOException { try { qp().context(value, type); } catch(final QueryException ex) { throw new BaseXException(ex); } } /** * Returns the query info. * @return query info */ public String info() { return info; } /** * Returns the serialization parameters. * @return serialization parameters * @throws IOException I/O Exception */ public String parameters() throws IOException { parse(); return qp.qc.serParams().toString(); } /** * Returns {@code true} if the query may perform updates. * @return updating flag * @throws IOException I/O Exception */ public boolean updating() throws IOException { parse(); return qp.updating; } /** * Executes the query. * @param out output stream * @param iter iterative evaluation * @param encode encode results (client/server communication, iterative processing) * @param full return full type information (only applicable to iterative evaluation) * @throws IOException I/O Exception */ public void execute(final OutputStream out, final boolean iter, final boolean encode, final boolean full) throws IOException { try { // parses the query and registers the process parse(); qp.register(ctx); // create serializer final Performance perf = jc().performance; qp.compile(); final QueryInfo qi = qp.qc.info; qi.compiling = perf.time(); final Iter ir = qp.iter(); qi.evaluating = perf.time(); // iterate through results int c = 0; final PrintOutput po = PrintOutput.get(encode ? new ServerOutput(out) : out); final SerializerOptions sopts = full ? SerializerMode.API.get() : qp.qc.serParams(); try(Serializer ser = Serializer.get(po, sopts)) { for(Item it; (it = ir.next()) != null;) { qp.qc.checkStop(); if(iter) { if(full) po.write(it.xdmInfo()); else po.write(it.typeId().asByte()); ser.reset(); ser.serialize(it); po.flush(); out.write(0); } else { ser.serialize(it); } c++; } } qi.serializing = perf.time(); // generate query info info = qi.toString(qp, po.size(), c, ctx.options.get(MainOptions.QUERYINFO)); } catch(final QueryException | JobException ex) { throw new BaseXException(ex); } catch(final StackOverflowError ex) { Util.debug(ex); throw new BaseXException(BASX_STACKOVERFLOW.desc); } finally { // close processor and unregisters the process if(qp != null) { if(parsed) { qp.close(); qp.unregister(ctx); parsed = false; } qp = null; popJob(); } } } /** * Initializes the query. * @throws IOException I/O Exception */ private void parse() throws IOException { if(parsed) return; final Performance perf = new Performance(); try { qp().parse(); } catch(final QueryException ex) { throw new BaseXException(ex); } qp.qc.info.parsing = perf.time(); parsed = true; } /** * Initializes and returns an instance of the query processor. * @return query processor */ private QueryProcessor qp() { if(parsed || qp == null) { qp = pushJob(new QueryProcessor(query, ctx)); parsed = false; } return qp; } }