/** * Import an XQuery library module from the given document. The namespace and preferred prefix of * the module are extracted from the module itself. The MIME type of the document is set to * "application/xquery" as a side-effect. * * @param module the non-XML document that holds the library module's source * @return this service, to chain calls * @throws DatabaseException if the module is an XML document, or the module declaration cannot be * found at the top of the document */ public QueryService importModule(Document module) { if (module instanceof XMLDocument) throw new DatabaseException("module cannot be an XML document: " + module); Matcher matcher = MODULE_DECLARATION_DQUOTE.matcher(module.contentsAsString()); if (!matcher.find()) { matcher = MODULE_DECLARATION_SQUOTE.matcher(module.contentsAsString()); if (!matcher.find()) throw new DatabaseException("couldn't find a module declaration at the top of " + module); } module.metadata().setMimeType("application/xquery"); String moduleNamespace = matcher.group(1); // TODO: should do URILiteral processing here to replace entity and character references and // normalize // whitespace, but since it seems that eXist doesn't do it either (bug?) there's no reason to // rush. Document prevModule = moduleMap.get(moduleNamespace); if (prevModule != null && !prevModule.equals(module)) throw new DatabaseException( "module " + moduleNamespace + " already bound to " + prevModule + ", can't rebind to " + module); moduleMap.put(moduleNamespace, module); return this; }
private void buildXQueryStaticContext(XQueryContext context, boolean importModules) throws XPathException { context.declareNamespaces(namespaceBindings.getCombinedMap()); for (Map.Entry<String, Document> entry : moduleMap.entrySet()) { context.importModule(entry.getKey(), null, "xmldb:exist:///db" + entry.getValue().path()); } }
private void buildXQueryDynamicContext( XQueryContext context, Object[] params, MutableDocumentSet docsToLock, boolean bindVariables) throws XPathException { context.setBackwardsCompatibility(false); context.setStaticallyKnownDocuments(docs); context.setBaseURI(baseUri == null ? new AnyURIValue("/db") : baseUri); if (bindVariables) { for (Map.Entry<QName, Object> entry : bindings.entrySet()) { context.declareVariable( new org.exist.dom.QName( entry.getKey().getLocalPart(), entry.getKey().getNamespaceURI(), entry.getKey().getPrefix()), convertValue(entry.getValue())); } if (params != null) for (int i = 0; i < params.length; i++) { Object convertedValue = convertValue(params[i]); if (docsToLock != null && convertedValue instanceof Sequence) { docsToLock.addAll(((Sequence) convertedValue).getDocumentSet()); } context.declareVariable("_" + (i + 1), convertedValue); } } }
/** * Statically analyze a query for various properties. * * @param query the query to analyze * @param params parameters for the query; if necessary parameters are left out they will be * listed as required variables in the analysis * @return a query analysis facet */ public QueryAnalysis analyze(String query, Object... params) { if (presub) query = presub(query, params); long t1 = System.currentTimeMillis(), t2 = 0, t3 = 0; DBBroker broker = null; try { broker = db.acquireBroker(); prepareContext(broker); final org.exist.source.Source source = buildQuerySource(query, params, "analyze"); final XQuery xquery = broker.getXQueryService(); final XQueryPool pool = xquery.getXQueryPool(); CompiledXQuery compiledQuery = pool.borrowCompiledXQuery(broker, source); try { AnalysisXQueryContext context; if (compiledQuery == null) { context = new AnalysisXQueryContext(broker, AccessContext.INTERNAL_PREFIX_LOOKUP); buildXQueryStaticContext(context, false); buildXQueryDynamicContext(context, params, null, false); t2 = System.currentTimeMillis(); compiledQuery = xquery.compile(context, source); t3 = System.currentTimeMillis(); } else { context = (AnalysisXQueryContext) compiledQuery.getContext(); t2 = System.currentTimeMillis(); } return new QueryAnalysis( compiledQuery, Collections.unmodifiableSet(context.requiredVariables), Collections.unmodifiableSet(context.requiredFunctions)); } finally { if (compiledQuery != null) pool.returnCompiledXQuery(source, compiledQuery); } } catch (XPathException e) { LOG.warn( "query compilation failed -- " + query + " -- " + (params == null ? "" : " with params " + Arrays.asList(params)) + (bindings.isEmpty() ? "" : " and bindings " + bindings)); throw new DatabaseException("failed to compile query", e); } catch (IOException e) { throw new DatabaseException("unexpected exception", e); } catch (PermissionDeniedException e) { throw new DatabaseException("permission denied", e); } finally { db.releaseBroker(broker); STATS.update(query, t1, t2, t3, 0, System.currentTimeMillis()); } }
private org.exist.source.Source buildQuerySource(String query, Object[] params, String cookie) { Map<String, String> combinedMap = namespaceBindings.getCombinedMap(); for (Map.Entry<String, Document> entry : moduleMap.entrySet()) { combinedMap.put("<module> " + entry.getKey(), entry.getValue().path()); } for (Map.Entry<QName, Object> entry : bindings.entrySet()) { combinedMap.put( "<var> " + entry.getKey(), null); // don't care about values, as long as the same vars are bound } combinedMap.put("<posvars> " + params.length, null); combinedMap.put("<cookie>", cookie); // TODO: should include statically known documents and baseURI too? return new StringSourceWithMapKey(query, combinedMap); }
/** * Clone this query service, optionally overriding the clone's namespace and variable bindings. If * the namespace bindings override or variable bindings override is specified, then that object is * cloned and used for its respective purpose. If an override is not specified, the bindings are * cloned from the original query service. * * @param nsBindingsOverride the namespace bindings to clone, or <code>null</code> to clone from * the original * @param varBindingsOverride the variable bindings to clone, or <code>null</code> to clone from * the original * @return a clone of this query service with bindings optionally overridden */ public QueryService clone(NamespaceMap nsBindingsOverride, Map<QName, ?> varBindingsOverride) { try { QueryService that = (QueryService) super.clone(); that.namespaceBindings = nsBindingsOverride != null ? nsBindingsOverride.clone() : that.namespaceBindings.clone(); if (varBindingsOverride == null) { that.bindings = new HashMap<QName, Object>(that.bindings); } else { that.bindings = new HashMap<QName, Object>(); for (Map.Entry<QName, ?> entry : varBindingsOverride.entrySet()) { that.let(entry.getKey(), entry.getValue()); } } that.moduleMap = new TreeMap<String, Document>(moduleMap); return that; } catch (CloneNotSupportedException e) { throw new RuntimeException("unexpected exception", e); } }
/** * Return a string that describes the statistics gathered for all the entries. * * @return a string describing the statistics gathered so far */ public synchronized String toString() { return toStringTop(entries.size()); }
/** Reset all gathered statistics back to zero. */ public synchronized void reset() { entries.clear(); }
/** * Get a list of all statistics entries for which data has been gathered. The list is a copy and * can be further manipulating without affecting the service. * * @return a list of all statistics entries */ public synchronized List<Entry> entries() { return new ArrayList<Entry>(entries.values()); }
synchronized Entry get(String query) { Entry entry = entries.get(query); if (entry == null) entries.put(query, entry = new Entry(query)); return entry; }
boolean isFreshFrom(Resource origin) { return !presub && bindings.isEmpty() && moduleMap.isEmpty() && (namespaceBindings == null || namespaceBindings.isFreshFrom(origin.namespaceBindings())); }
ItemList executeQuery(String query, WrapperFactory wrapperFactory, Object[] params) { long t1 = System.currentTimeMillis(), t2 = 0, t3 = 0, t4 = 0; if (presub) query = presub(query, params); DBBroker broker = null; try { broker = db.acquireBroker(); prepareContext(broker); if (overrideDocs != null) docs = overrideDocs; final org.exist.source.Source source = buildQuerySource(query, params, "execute"); final XQuery xquery = broker.getXQueryService(); final XQueryPool pool = xquery.getXQueryPool(); CompiledXQuery compiledQuery = pool.borrowCompiledXQuery(broker, source); MutableDocumentSet docsToLock = new DefaultDocumentSet(); if (docs != null) docsToLock.addAll(docs); if (base != null) docsToLock.addAll(base.getDocumentSet()); try { XQueryContext context; if (compiledQuery == null) { context = xquery.newContext(AccessContext.INTERNAL_PREFIX_LOOKUP); buildXQueryStaticContext(context, true); } else { context = compiledQuery.getContext(); // static context already set } buildXQueryDynamicContext(context, params, docsToLock, true); t2 = System.currentTimeMillis(); if (compiledQuery == null) { compiledQuery = xquery.compile(context, source); t3 = System.currentTimeMillis(); } docsToLock.lock(broker, false, false); try { return new ItemList( xquery.execute(wrap(compiledQuery, wrapperFactory, context), base), namespaceBindings.extend(), db); } finally { docsToLock.unlock(false); t4 = System.currentTimeMillis(); } } finally { if (compiledQuery != null) pool.returnCompiledXQuery(source, compiledQuery); } } catch (XPathException e) { LOG.debug( "query execution failed -- " + query + " -- " + (params == null ? "" : " with params " + Arrays.asList(params)) + (bindings.isEmpty() ? "" : " and bindings " + bindings)); throw new DatabaseException("failed to execute query", e); } catch (IOException e) { throw new DatabaseException("unexpected exception", e); } catch (LockException e) { throw new DatabaseException("deadlock", e); } catch (PermissionDeniedException e) { throw new DatabaseException("permission denied", e); } finally { db.releaseBroker(broker); STATS.update(query, t1, t2, t3, t4, System.currentTimeMillis()); } }
/** * Import the same modules into this query service as imported by the given query service. This is * a one-time copy; further imports into either query service won't affect the other one. * * @param that the query service to copy module imports from * @return this service, to chain calls */ public QueryService importSameModulesAs(QueryService that) { moduleMap.putAll(that.moduleMap); return this; }
/** * Bind a variable to the given value within all query expression evaluated subsequently. * * @param variableName the qualified name of the variable to bind * @param value the value the variable should take * @return this service, to chain calls */ public QueryService let(QName variableName, Object value) { bindings.put(variableName, value); return this; }