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;
}
}