XQJ: Same Instance Methods Dont Always Fire
Added by Anonymous almost 18 years ago
Legacy ID: #4061209 Legacy Poster: Monte Hansen (monte-hansen)
I'm seeing strange results with the xqj extended callout of instance based methods. Given the below template, the instance methods are not always being fired (currently, only the last 2 addParameter methods are invoked). Strange though because at some point it did work. The method in question (addParameter) is inherited from an abstract class. I've even tried overriding the method at the subclass, and changing it's type to String; no change. For funzzies, I changed the method to static and it (appropriatly) complained with XPST0017: Cannot find a matching 2-argument function. I then ran the same query using the axyana library and (and equiv. java code) and it executed all methods as expected. I then rolled back from the latest patched jars but no luck. Any ideas? Monte ---XQuery Template--- declare namespace dao="java:com.company.division.department.dao.entities.AppDataAccessObject"; declare variable $namesearchcriteria as xs:string external; declare variable $soundexoption as xs:string external; declare variable $addresslinesearchcriteria as xs:string external; declare variable $citysearchcriteria as xs:string external; declare variable $statesearchcriteria as xs:string external; declare variable $countrysearchcriteria as xs:string external; declare variable $telephonesearchcriteria as xs:string external; <Page> <Results> { let $o := dao:new() let $x := dao:addParameter( $o, $vendornamesearchcriteria ) let $x := dao:addParameter( $o, $soundex ) let $x := dao:addParameter( $o, $addresslinesearchcriteria ) let $x := dao:addParameter( $o, $citysearchcriteria ) let $x := dao:addParameter( $o, $statesearchcriteria ) let $x := dao:addParameter( $o, $countrysearchcriteria ) let $x := dao:addParameter( $o, $telephonesearchcriteria ) let $d := dao:serializeResults( $o, "{? = call oraschema.package.RefCursorProcedure( ?, ?, ?, ?, ?, ?, ? )}" ) return $d//row } </Results> </Page> ---Java Method--- public void addParameter( Object parameter ) { System.out.println( "Added parameter: " + parameter ); parms.add( parameter ); }
Replies (5)
Please register to reply
RE: XQJ: Same Instance Methods Dont Always Fi - Added by Anonymous almost 18 years ago
Legacy ID: #4061537 Legacy Poster: Michael Kay (mhkay)
Saxon won't call a function (and this includes an external function) unless it actually needs to know the result. This is common property of functional programming languages, and it means that it's a bad idea to write functions that have side-effects, unless the side-effect is only for diagnostic or instrumentation purposes. You can usually force the function to be called by writing an expression that pretends to make use of the result, for example let $o := dao:new() let $x := ( dao:addParameter( $o, $vendornamesearchcriteria ), dao:addParameter( $o, $soundex ), dao:addParameter( $o, $addresslinesearchcriteria ), dao:addParameter( $o, $citysearchcriteria ), dao:addParameter( $o, $statesearchcriteria ), dao:addParameter( $o, $countrysearchcriteria ), dao:addParameter( $o, $telephonesearchcriteria )) let $o := ($o, $x) let $d := dao:serializeResults( $o, "{? = call oraschema.package.RefCursorProcedure( ?, ?, ?, ?, ?, ?, ? )}" ) Even if you do this, however, it's best not to rely on the external functions being called in any particular order. A better approach is to design your external Java class so that its methods have no side-effects. In this example, since the various parameters are strings, you could construct the dao object in a single call that takes an XML element structure as input, with the various parameters all being expressed as attributes of the element. Alternatively, you could make a trivial change to the addParameter method so that it returns the new dao object; you would then be able to write the sequence of calls as let $o := dao:new() let $o := dao:addParameter( $o, $vendornamesearchcriteria ) let $o := dao:addParameter( $o, $soundex ) etc; and Saxon would be forced to evaluate the calls in sequence because the result of each call depends (apparently) on the result of the previous one. Michael Kay
RE: XQJ: Same Instance Methods Dont Always Fire - Added by Anonymous almost 18 years ago
Legacy ID: #4062783 Legacy Poster: Monte Hansen (monte-hansen)
OK, that helps. At least I can work around it. Not sure if that's a choice of the implementation, or the spec. For internal processing I can see the benefit for optimization, but for external processing I suspect the consumers of the API would prefer it didn't. I would invite you to consider that. To work around it I chose this (ch)easy approach =) let $parms := concat( $namesearchcriteria, "|", $soundex, ... ) let $o := dao:new() let $parms := dao:addParameters( $o, $parms, "|" ) let $parms := concat( $parms, "" ) let $d := dao:serializeResults( $o, "{? = call gapappl.ap_query.FindVendors( ?, ?, ?, ?, ?, ?, ? )}" ) Thanks again for your help, Monte
RE: XQJ: Same Instance Methods Dont Always Fi - Added by Anonymous almost 18 years ago
Legacy ID: #4062798 Legacy Poster: Michael Kay (mhkay)
The XQuery spec leaves the behaviour of external functions entirely implementation-defined. Saxon takes a few simple steps to guard against over-zealous optimizations of expressions that use external functions, for example it carefully avoids inferring that because the function returns void, there is no point in calling it, and it treats external function calls in the same way as identity-sensitive operations such as element constructors, which means they won't be moved out of loops or pre-evaluated at compile time. A more general treatment of side-effects is out of scope, however. In particular, Saxon will not evaluate an unreferenced variable just because doing so might cause an external function to be invoked, nor will it allow the presence of external function calls to affect the order of execution. Perhaps if procedural extensions such as those proposed in XQueryP [1] are accepted into the language, this will provide the semantic framework in which a more predictable handling of side-effects becomes possible. In the meantime the best advice is to design your external functions to have no side-effects. There's nothing new here, incidentally: the potential dangers of mixing functions-with-side-effects into a declarative language have been well understood in the XSLT community for many years, not to mention other more traditional functional programming languages, and you'll find similar advice in most textbooks. [1] http://www.ximep-2006.org/papers/Paper-Chamberlin-Carey.pdf
RE: XQJ: Same Instance Methods Dont Always Fire - Added by Anonymous almost 18 years ago
Legacy ID: #4063367 Legacy Poster: Monte Hansen (monte-hansen)
Language theory aside, it will be a common trap for authors to fall into. And doing a bogus assignment on a let'er for every void method invoked is awkward at best (for externals). I suspect other xqj users will make similar comments in the future. That said, I don't think it's working quite as you intended. I'm finding unpredictable results. For instance, I have a template with numerous blocks like below. With each block I'll sprinkle code to fake the processing engine, and the first 5 will execute as expected. Then I make a correction to the 6th block and suddently it processes only block #3. I've had several episodes like that. Is there a way to turn this feature off? declare variable $gap-o := gap:new(); <Stuff> <Thing1> { let $void := gap:clearParameters( $gap-o ) let $void := concat( "voidhack", $void ) let $void := gap:addParameter( $gap-o, $thing1 ) let $void := concat( "voidhack", $void ) let $void := gap:addParameter( $gap-o, $thing2 ) let $void := concat( "voidhack", $void ) let $d := gap:serializeResultset( $gap-o, "{? = call schema.pkg.Proc(?,?)}" ) return $d//row } </Thing1> <Thing2> { ... } </Thing2> </Stuff>
RE: XQJ: Same Instance Methods Dont Always Fi - Added by Anonymous almost 18 years ago
Legacy ID: #4063421 Legacy Poster: Michael Kay (mhkay)
>That said, I don't think it's working quite as you intended. I'm finding unpredictable results. I find the results hard to predict as well. That's why I advise people not to use external functions that have side effects, especially not functions that exist only for the sake of their side effects. If you return a result, and use the result in the query, then things should work as expected. It's not a case of turning a feature off. You're asking for a language that has procedural semantics, and XQuery isn't such a language.
Please register to reply