package org.basex.core.cmd; import org.basex.core.*; import static org.basex.core.Text.*; import org.basex.data.Result; import org.basex.io.IOFile; import org.basex.io.out.ArrayOutput; import org.basex.io.out.BufferOutput; import org.basex.io.out.NullOutput; import org.basex.io.out.PrintOutput; import org.basex.io.serial.DOTSerializer; import org.basex.io.serial.Serializer; import org.basex.query.QueryException; import org.basex.query.QueryProcessor; import org.basex.query.item.Item; import org.basex.query.iter.Iter; import static org.basex.query.util.Err.XPSTACK; import org.basex.util.Performance; import org.basex.util.Util; import java.io.IOException; /** * Abstract class for database queries. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ abstract class AQuery extends Command { /** Query result. */ Result result; /** Query processor. */ private QueryProcessor qp; /** Query exception. */ private QueryException qe; /** Initial parsing time. */ private long init; /** Parsing time. */ private long pars; /** Compilation time. */ private long comp; /** Evaluation time. */ private long eval; /** Printing time. */ private long prnt; /** * Protected constructor. * @param flags command flags * @param arg arguments */ AQuery(final int flags, final String... arg) { super(flags, arg); } /** * Evaluates the specified query. * @param query query * @return success flag */ final boolean query(final String query) { final Performance p = new Performance(); String err; String inf = ""; if(qe != null) { err = qe.getMessage(); } else { try { final boolean serial = prop.is(Prop.SERIALIZE); long hits = 0; int updates = 0; final int runs = Math.max(1, prop.num(Prop.RUNS)); for(int r = 0; r < runs; ++r) { // reuse existing processor instance if(r != 0) qp = null; qp = queryProcessor(query, context); qp.parse(); pars += init + p.getTime(); init = 0; if(r == 0) plan(false); qp.compile(); comp += p.getTime(); if(r == 0) plan(true); final PrintOutput po = r == 0 && serial ? out : new NullOutput(); final Serializer ser; if(prop.is(Prop.CACHEQUERY)) { result = qp.execute(); eval += p.getTime(); ser = qp.getSerializer(po); result.serialize(ser); hits = result.size(); } else { final Iter ir = qp.iter(); eval += p.getTime(); hits = 0; Item it = ir.next(); ser = qp.getSerializer(po); while(it != null) { checkStop(); ser.openResult(); it.serialize(ser); ser.closeResult(); it = ir.next(); ++hits; } } updates = qp.updates(); ser.close(); qp.close(); prnt += p.getTime(); } // dump some query info if(prop.is(Prop.QUERYINFO)) evalInfo(query, hits, updates, runs); out.flush(); return info(NL + QUERY_EXECUTED_X, perf.getTimer(runs)); } catch(final QueryException ex) { Util.debug(ex); err = ex.getMessage(); } catch(final IOException ex) { Util.debug(ex); err = ex.getMessage(); } catch(final ProgressException ex) { err = INTERRUPTED; // store any useful info (e.g. query plan): inf = info(); } catch(final RuntimeException ex) { Util.debug(qp.info()); throw ex; } catch(final StackOverflowError ex) { Util.debug(ex); err = XPSTACK.desc; } // close processor after exceptions if(qp != null) try { qp.close(); } catch(final QueryException ex) { } } error(err); if(Util.debug || err.startsWith(INTERRUPTED)) { info(NL); info(QUERY_CC + query); info(qp.info()); info(inf); } return false; } /** * Checks if the query might perform updates. * @param ctx database context * @param qu query * @return result of check */ final boolean updating(final Context ctx, final String qu) { // keyword found; parse query to get sure try { final Performance p = new Performance(); qp = progress(new QueryProcessor(qu, ctx)); qp.parse(); init = p.getTime(); return qp.ctx.updating; } catch(final QueryException ex) { Util.debug(ex); qe = ex; if(qp != null) try { qp.close(); } catch(final QueryException e) { } return false; } } /** * Performs the first argument as XQuery and returns a node set. */ final void queryNodes() { try { result = queryProcessor(args[0], context).queryNodes(); } catch(final QueryException ex) { Util.debug(ex); qp = null; error(ex.getMessage()); } } /** * Returns a query processor instance. * @param query query string * @param ctx database context * @return query processor */ private QueryProcessor queryProcessor(final String query, final Context ctx) { if(qp == null) qp = progress(new QueryProcessor(query, ctx)); return qp; } @Override public final Result result() { return result; } /** * Adds evaluation information to the information string. * @param query query string * @param hits information * @param updates updated items * @param runs number of runs */ private void evalInfo(final String query, final long hits, final long updates, final int runs) { final long total = pars + comp + eval + prnt; info(NL); info(QUERY_CC + QueryProcessor.removeComments(query, Integer.MAX_VALUE)); info(qp.info()); info(PARSING_CC + Performance.getTimer(pars, runs)); info(COMPILING_CC + Performance.getTimer(comp, runs)); info(EVALUATING_CC + Performance.getTimer(eval, runs)); info(PRINTING_CC + Performance.getTimer(prnt, runs)); info(TOTAL_TIME_CC + Performance.getTimer(total, runs) + NL); info(HITS_X_CC + hits + ' ' + (hits == 1 ? ITEM : ITEMS)); info(UPDATED_CC + updates + ' ' + (updates == 1 ? ITEM : ITEMS)); info(PRINTED_CC + Performance.format(out.size())); } /** * Creates query plans. * @param c compiled flag */ private void plan(final boolean c) { if(c != prop.is(Prop.COMPPLAN)) return; // show dot plan BufferOutput bo = null; try { if(prop.is(Prop.DOTPLAN)) { final String path = context.prop.get(Prop.QUERYPATH); final String dot = path.isEmpty() ? "plan.dot" : new IOFile(path).name().replaceAll("\\..*?$", ".dot"); bo = new BufferOutput(dot); final DOTSerializer d = new DOTSerializer(bo, prop.is(Prop.DOTCOMPACT)); qp.plan(d); d.close(); if(prop.is(Prop.DOTDISPLAY)) new ProcessBuilder(prop.get(Prop.DOTTY), dot).start(); } // show XML plan if(prop.is(Prop.XMLPLAN)) { final ArrayOutput ao = new ArrayOutput(); qp.plan(Serializer.get(ao)); info(NL + QUERY_PLAN_C); info(ao.toString()); } } catch(final Exception ex) { Util.stack(ex); } finally { if(bo != null) try { bo.close(); } catch(final IOException ex) { } } } @Override public void build(final CommandBuilder cb) { cb.init().xquery(0); } @Override public boolean stoppable() { return true; } }