Bug #6149
closed$connection?close() throws NullPointerException
100%
Description
When we want to close an Oracle db using the instruction
<saxon:do action="$connection?close()"/>
we get the following error message:
java.lang.NullPointerException
at net.sf.saxon.expr.LookupExpression$LookupElaborator.lambda$elaborateForPull$14(LookupExpression.java:618)
at net.sf.saxon.expr.SequenceMapper.map(SequenceMapper.java:51)
at net.sf.saxon.expr.MappingIterator.next(MappingIterator.java:81)
at net.sf.saxon.expr.CardinalityCheckingIterator.<init>(CardinalityCheckingIterator.java:55)
at net.sf.saxon.expr.CardinalityChecker.checkCardinality(CardinalityChecker.java:257)
at net.sf.saxon.expr.CardinalityChecker$CardinalityCheckerElaborator.lambda$elaborateForPull$0(CardinalityChecker.java:502)
at net.sf.saxon.expr.elab.PullElaborator.lambda$elaborateForItem$1(PullElaborator.java:54)
at net.sf.saxon.expr.ItemChecker$ItemCheckerElaborator.lambda$elaborateForItem$1(ItemChecker.java:462)
at net.sf.saxon.expr.DynamicFunctionCall$DynamicFunctionCallElaborator.lambda$elaborateForPull$2(DynamicFunctionCall.java:319)
at net.sf.saxon.expr.elab.EagerPullEvaluator.evaluate(EagerPullEvaluator.java:37)
at com.saxonica.xsltextn.instruct.DoInstr$DoInstructionElaborator.lambda$elaborateForPush$0(DoInstr.java:91)
at net.sf.saxon.expr.instruct.Choose$ChooseExprElaborator.lambda$elaborateForPush$10(Choose.java:1168)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:269)
at net.sf.saxon.expr.instruct.CallTemplate$CallTemplateElaborator.lambda$elaborateForPush$1(CallTemplate.java:633)
at net.sf.saxon.expr.instruct.Block$BlockElaborator.lambda$elaborateForPush$4(Block.java:895)
at net.sf.saxon.expr.LetExpression$LetExprElaborator.lambda$elaborateForPush$6(LetExpression.java:942)
at net.sf.saxon.expr.TryCatch$TryCatchElaborator.lambda$elaborateForPush$1(TryCatch.java:340)
at net.sf.saxon.expr.instruct.NamedTemplate.expand(NamedTemplate.java:269)
at net.sf.saxon.trans.XsltController.callTemplate(XsltController.java:872)
at net.sf.saxon.s9api.Xslt30Transformer.callTemplate(Xslt30Transformer.java:503)
at net.sf.saxon.Transform.processFile(Transform.java:1375)
at net.sf.saxon.Transform.doTransform(Transform.java:879)
at net.sf.saxon.Transform.main(Transform.java:83)
Fatal error during transformation: java.lang.NullPointerException: (no message)
2 additional observations:
- <sql:close connection="$connection"/> works fine
- there is no error when closing a sqlite database
Updated by Michael Kay over 1 year ago
It's always difficult to investigate problems with just a stack trace an no repro, but I think I have a bit of an idea what's going on here. I suspect it has nothing to do with the SQL code, and everything to do with the Saxon extension that enables you to call methods on external objects using the $connection?close()
syntax. It looks to me as if the expression that achieves this -- the ObjectLookupExpression -- hasn't undergone all the changes that should have been made to support expression elaboration in Saxon 12. My next step is to look at the tests in this area and see if they need beefing up. It's possible that our testing has relied too heavily on the SQL use cases, which isn't ideal because these tests are often omitted because of the complex configuration needed.
As a workaround, I suggest you revert to the old ways of calling the close()
method on the connection object.
(having said that, I find the actual NullPointerException rather mystifying. The code has failed executing the last line of
if (!(rhsVal instanceof StringValue)) {
throw new XPathException(....);
}
String key = ((StringValue) rhsVal).getStringValue();
and it's hard to see how that can NPE.
Updated by Michael Kay over 1 year ago
I haven't yet worked out how this is failing, but I have made some progress of sorts. The path that's failing is only supposed to be executed in the case where we can't establish statically (during type-checking) that this expression is a specific call of a specific Java method. But the introduction of expression elaboration means that we are always taking this path. We're still doing the static analysis to reduce the expression to a simple Java method call, and then we're ignoring the result of this analysis and evaluating the expression on the fallback path. But this doesn't explain why the fallback path is failing.
Updated by Michael Kay over 1 year ago
I am wondering if the Oracle SQL connection object provides more than one overload of the close() method, in which case the name "close" will not be available in the map; instead it is necessary to call $connection.close_0()
.
I would expect this to result in an explicit error message rather than an NPE, but I'll do some experiments.
Meanwhile I have implemented the elaboration code for the case where the Java method can be determined statically. However, it occurs to me that this is relying on the static type of the Java object, rather than its dynamic type, which may have additional methods available... The documentation at https://www.saxonica.com/documentation11/index.html#!extensibility/extension-functions-J/reflexive-functions/instance-methods needs updating.
Updated by Michael Kay over 1 year ago
Aha! I see at https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html that the OracleConnection class has several overloads of the close() method, which are going to be visible when we rely on the dynamic type of the object.
Updated by Michael Kay over 1 year ago
OK, I've managed to construct a repro. I'm getting the NPE two lines later:
java.lang.NullPointerException: Cannot invoke "net.sf.saxon.om.GroundedValue.iterate()" because the return value of "net.sf.saxon.ma.map.MapItem.get(net.sf.saxon.value.AtomicValue)" is null
at net.sf.saxon.expr.LookupExpression$LookupElaborator.lambda$elaborateForPull$14(LookupExpression.java:620)
at net.sf.saxon.expr.SequenceMapper.map(SequenceMapper.java:51)
at net.sf.saxon.expr.MappingIterator.next(MappingIterator.java:81)
This is reverting to the original 12.x code without expression elaboration added; I still don't see how an NPE is possible on line 618, but we're close enough now I think.
The addition of expression elaboration fixes this and causes the statically-determined close() method to be called, it never gets as far as noticing that the dynamic object has an overload of this.
But we should add a check to prevent the NPE on the dynamic path anyway.
Updated by Michael Kay over 1 year ago
The fix for this bug also slips in an incidental change that I found useful when constructing my test cases: conversion is now possible between the XPath xs:string
type and Saxon's net.sf.saxon.str.UnicodeString
, in both directions.
Updated by Michael Kay over 1 year ago
- Status changed from New to Resolved
- Applies to branch 12, trunk added
- Fix Committed on Branch 12, trunk added
- Platforms Java added
Updated by Johan Gheys over 1 year ago
Sorry, in my naivety I thought an error message was enough, but of course a repro is always better (but not so obvious in this case). But the problem is solved even before I have a repro ready. Just amazing!
Updated by O'Neil Delpratt about 1 year ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in Maintenance Release 12.4 added
Bug fix applied in the Saxon 12.4 maintenance release
Please register to edit this issue