package org.basex.query; import static org.basex.core.Text.*; import static org.basex.util.Token.*; import java.util.*; import org.basex.core.*; import org.basex.core.locks.*; import org.basex.io.*; import org.basex.query.func.*; import org.basex.query.scope.*; import org.basex.query.util.*; import org.basex.query.value.item.*; import org.basex.query.var.*; import org.basex.util.*; import org.basex.util.list.*; /** * This class remembers descriptive query information sent back to the client. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ public final class QueryInfo { /** Verbose info. */ private final boolean verbose; /** Read locked databases. */ public Locks locks; /** Parsing time (nano seconds). */ public long parsing; /** Compilation time (nano seconds). */ public long compiling; /** Evaluation time (nano seconds). */ public long evaluating; /** Serialization time (nano seconds). */ public long serializing; /** Query. */ String query; /** Runtime flag. */ boolean runtime; /** Compilation info. */ private final TokenList compile = new TokenList(0); /** Evaluation info. */ private final TokenList evaluate = new TokenList(0); /** String length of evaluation info. */ private int evalSize; /** * Constructor. * @param qc query context */ QueryInfo(final QueryContext qc) { verbose = qc.context.options.get(MainOptions.QUERYINFO) || Prop.debug; } /** * Adds some compilation info. * @param string evaluation info * @param ext text text extensions */ void compInfo(final String string, final Object... ext) { if(verbose) { String info = Util.info(string, ext); if(runtime) { info = "RUNTIME: " + info; if(Prop.debug) Util.stack(info); } compile.add(info); } } /** * Adds some evaluation info. * @param string evaluation info */ void evalInfo(final String string) { if(verbose) { final byte[] token = chop(token(string.replaceAll("\r?\n", "|")), 1 << 14); synchronized(evaluate) { if(evalSize < (1 << 22)) { evaluate.add(token); evalSize += token.length; } } } } /** * Returns detailed query information. * @param qp query processor * @param printed printed bytes * @param hits number of returned hits * @param detailed return detailed query info * @return query string */ public String toString(final QueryProcessor qp, final long printed, final long hits, final boolean detailed) { final int runs = Math.max(1, qp.qc.context.options.get(MainOptions.RUNS)); final TokenBuilder tb = new TokenBuilder(); final long total = parsing + compiling + evaluating + serializing; if(detailed) { final int up = qp.updates(); tb.add(toString(qp.qc)).add(NL); tb.add(PARSING_CC).add(Performance.getTime(parsing, runs)).add(NL); tb.add(COMPILING_CC).add(Performance.getTime(compiling, runs)).add(NL); tb.add(EVALUATING_CC).add(Performance.getTime(evaluating, runs)).add(NL); tb.add(PRINTING_CC).add(Performance.getTime(serializing, runs)).add(NL); tb.add(TOTAL_TIME_CC).add(Performance.getTime(total, runs)).add(NL).add(NL); tb.add(HITS_X_CC + hits).add(' ').add(hits == 1 ? ITEM : ITEMS).add(NL); tb.add(UPDATED_CC + up).add(' ').add(up == 1 ? ITEM : ITEMS).add(NL); tb.add(PRINTED_CC).add(Performance.format(printed)).add(NL); if(locks != null) { tb.add(READ_LOCKING_CC).addExt(locks.reads).add(NL); tb.add(WRITE_LOCKING_CC).addExt(locks.writes).add(NL); } } final IO baseIO = qp.sc.baseIO(); final String name = baseIO == null ? "" : " \"" + baseIO.name() + '"'; tb.addExt(NL + QUERY_EXECUTED_X_X, name, Performance.getTime(total, runs)); return tb.toString(); } /** * Returns detailed compilation and evaluation information. * @param qc query context * @return string */ String toString(final QueryContext qc) { final TokenBuilder tb = new TokenBuilder(); if(query != null) { final String qu = QueryProcessor.removeComments(query, Integer.MAX_VALUE); tb.add(NL).add(QUERY).add(COL).add(NL).add(qu).add(NL); } if(!compile.isEmpty()) { tb.add(NL).add(COMPILING).add(COL).add(NL); for(final byte[] line : compile) tb.add(LI).add(line).add(NL); tb.add(NL).add(OPTIMIZED_QUERY).add(COL).add(NL); tb.add(qc.root == null ? qc.funcs.toString() : usedDecls(qc.root)).add(NL); } if(!evaluate.isEmpty()) { tb.add(NL).add(EVALUATING).add(COL).add(NL); for(final byte[] line : evaluate) tb.add(LI).add(line).add(NL); } return tb.toString(); } /** * Serializes all functions and variables reachable from the given main module. * @param mod module to start from * @return the string representation */ private static String usedDecls(final MainModule mod) { final IdentityHashMap<Scope, Object> map = new IdentityHashMap<>(); final StringBuilder sb = new StringBuilder(); mod.visit(new ASTVisitor() { @Override public boolean staticVar(final StaticVar var) { if(map.put(var, var) == null) { var.visit(this); sb.append(var).append(NL); } return true; } @Override public boolean staticFuncCall(final StaticFuncCall call) { final StaticFunc f = call.func(); if(map.put(f, f) == null) { f.visit(this); sb.append(f).append(NL); } return true; } @Override public boolean inlineFunc(final Scope sub) { if(map.put(sub, sub) == null) sub.visit(this); return true; } @Override public boolean funcItem(final FuncItem func) { if(map.put(func, func) == null) func.visit(this); return true; } }); return sb.append(mod).toString(); } }