Problem with "IS EMPTY" in JPQL queries


william.martel@...
 

Hi, I believe I have found a bug in the JPQLParser class, but I'm not entirely sure.

Considering a User class, with a nullable 1-1 link to a UserAccount class, and a collection of Contract objects in the UserAccount class, and a JPQL query with the following form:

SELECT u FROM package.to.my.class.User u LEFT JOIN u.userAccount ua WHERE ua.contracts IS EMPTY

The code that parses the "IS EMPTY" clause is as follows (JPQLParser.processRelationalExpression()):

else if (lexer.parseStringIgnoreCase("EMPTY"))
{
// Convert IS EMPTY to a method call of "size()==0" on collection/map
Node sizeNode = new Node(NodeType.INVOKE, "size");
inputNode.insertChildNode(sizeNode);
Node isEmptyNode = new Node(NodeType.OPERATOR, not ? "!=" : "==");
isEmptyNode.appendChildNode(inputNode);
Node zeroNode = new Node(NodeType.LITERAL, 0);
isEmptyNode.appendChildNode(zeroNode);
stack.push(isEmptyNode);
}

Debugging through this code, I find that there is a variable "inputRootNode" which essentially contains ua.contracts, and the variable "inputNode" only contains the last part of this chain, in this case "contracts". However, in the else if statement to handle "EMPTY", the isEmptyNode appends inputNode as a child, instead of inputRootNode, which causes the node structure to end up like this:

[OPERATOR : ==.

    [IDENTIFIER : contracts.

        [INVOKE : size]],

    [LITERAL : 0]]

Notice how we lost the "ua." prefix for contracts. Then, at the end of the query compilation, we get a NucleusUserException, with the message "Class name contracts could not be resolved".

Replacing the line "isEmptyNode.appendChildNode(inputNode)" by "isEmptyNode.appendChildNode(inputRootNode)", similar to the previous if statement that handles the "NULL" case, seems to resolve the problem, but I'm not sure if that would have an impact on different queries or if there's more to it than that, but I thought I'd point it out. Maybe there is something wrong/not supported in the query I'm using?


Andy
 

Correct (so then it would be consistent with the IS NULL block just above).
You find whether that has an effect by running the test suites under the "tests" repository, particularly those under "jpa". Unlikely to make a difference to existing cases


william.martel@...
 

Thank you, I'll take moment this week-end and see if I can run those tests, and open a PR for the fix. For anybody else that stumbles onto this thread, note that you can as a workaround use the SIZE() method, for example:

SELECT package.to.the.class.ClassName c WHERE c.someCollection IS EMPTY

can be written as:
SELECT package.to.the.class.ClassName c WHERE SIZE(c.someCollection) = 0


Andy
 

FYI, the tests under jpa/TCK_1.0 (JPA TCK v1.0) are all fine, no need to run that