Stopping a running query programatically
Added by Reece Dunn over 4 years ago
Hi,
I'm using the s9api to run XSLT, XPath, and XQuery from within an IDE. What is the recommended way of stopping the query that is still running?
My current approach is to always attach a TraceListener, and throw a custom "query cancelled" exception in one of the callback methods if the user has requested that the query be stopped. This works, but can sometimes result in an "Internal error evaluating function" runtime error. I can resolve that by checking the cause of the runtime error, but I'm wondering if there is a cleaner mechanism supported by the s9api.
Kind regards, Reece
Replies (2)
RE: Stopping a running query programatically - Added by Michael Kay over 4 years ago
Well, firstly there's no way of interrupting some queries. For example if the query is descendant::node()
, then nothing is going to stop it scanning the entire document. But even with a very large document, that's actually a pretty fast query, so perhaps you don't need to. You're more likely to want to interrupt a complex join query.
Rather than use a general-purpose TraceListener, a more elegant strategy might be to use the CodeInjector hook. It's probably the case that most long-running-queries involve a filter expression xxx[predicate]
, and that looking for interrupts on each evaluation of the predicate will do the job.
You can do this for example at the level of the XSLT CompilerInfo or the XQuery StaticQueryContext. Implement the method
Expression inject(Expression exp, StaticContext env, int construct, StructuredQName qName);
and if exp
is a FilterExpression
, modify the predicate of the FilterExpression to add a check to see if you've been interrupted, and if so throw an XPathException -- ((FilterExpression)exp).setFilter(makeModifiedFilter(((FilterExpression)exp).getFilter())
.
So what should makeModifiedFilter()
return? You want it to add very little overhead because you're on a performance critical path. I think I would define a new subclass of UnaryExpression
, with an effectiveBooleanValue()
method that checks for interruption and then calls effectiveBooleanValue() on its only subexpression.
Thinking about it, you can actually use this technique to interrupt even a simple AxisExpression
like descendant::node()
, but it would get quite tricky; you would have to replace the AxisExpression with a subclass whose iterator() method wraps super.iterator() with a wrapping iterator that checks the interrupt flag on each call of next()
. And you then hit the problem that AxisIterator.next()
throws no checked exceptions.
RE: Stopping a running query programatically - Added by Reece Dunn over 4 years ago
Thanks for the advice on using CodeInjector
. I'll look into incorporating that logic, making sure to minimize the performance impact of any changes.
Please register to reply