Project

Profile

Help

Bug #6149

closed

$connection?close() throws NullPointerException

Added by Johan Gheys 9 months ago. Updated 5 months ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Category:
Saxon extensions
Sprint/Milestone:
-
Start date:
2023-07-31
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
12, trunk
Fix Committed on Branch:
12, trunk
Fixed in Maintenance Release:
Platforms:
Java

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
Actions #1

Updated by Michael Kay 9 months 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.

Actions #2

Updated by Michael Kay 9 months 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.

Actions #3

Updated by Michael Kay 9 months 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.

Actions #4

Updated by Michael Kay 9 months 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.

Actions #5

Updated by Michael Kay 9 months 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.

Actions #6

Updated by Michael Kay 9 months 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.

Actions #7

Updated by Michael Kay 9 months ago

  • Status changed from New to Resolved
  • Applies to branch 12, trunk added
  • Fix Committed on Branch 12, trunk added
  • Platforms Java added
Actions #8

Updated by Johan Gheys 9 months 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!

Actions #9

Updated by O'Neil Delpratt 5 months 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

Also available in: Atom PDF