eclipselink
eclipselink copied to clipboard
Regression in 2.7.5/3.0.2: Wrong parameter type expected in "coalesce"
EclipseLink 2.7.9/3.0.2
Any database platform.
String jsql = "select c from Country c where c.name is not null and coalesce(c.iso2, :p1) != 'DE'";
TypedQuery<Country> query = em.createQuery(jsql, Country.class);
query.setParameter("p1", "DE"); // exception!!
This code throws this exception:
Exception in thread "main" java.lang.IllegalArgumentException: You have attempted to set a value of type class java.lang.String for parameter p1 with expected type of class java.lang.Boolean from query string select c from Country c where c.name is not null and coalesce(c.iso2, :p1) != 'DE'.
at org.eclipse.persistence.internal.jpa.QueryImpl.setParameterInternal(QueryImpl.java:946)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.setParameter(EJBQueryImpl.java:609)
at de.wirthedv.test.eclipselink.TestExistsWithCoalesce.test(TestExistsWithCoalesce.java:24)
at de.wirthedv.test.eclipselink.util.AbstractTestCase.run(AbstractTestCase.java:22)
at de.wirthedv.test.eclipselink.TestExistsWithCoalesce.main(TestExistsWithCoalesce.java:13)
This bug was introduced in version 2.7.5
. I tested with 2.7.4
and below, it is working. I tested with 2.7.5
, it is failing.
I tried different queries to localize the issue. The issue arises when you have a coalesce
with a parameter (first or second parameter) and an expression before that (in the example it is c.name is not null
).
The bug was introduced in the commit: 8a232d422c15b1d15e757aed1282edc4754c1c9e
File: ExpressionBuilderVisitor.java
Method: public void visit(CoalesceExpression expression) {
The line Class<?> coalesceType = type[0];
was introduced, which caches the wrong type. The correct type is not known at this point in time. It will be known only inside the following for-loop.
I think the problematic method should look like this:
@Override
public void visit(CoalesceExpression expression) {
List<Expression> expressions = new ArrayList<Expression>();
List<Class<?>> types = new LinkedList<Class<?>>();
// Create the Expression for each scalar expression
for (org.eclipse.persistence.jpa.jpql.parser.Expression child : expression.getExpression().children()) {
child.accept(this);
expressions.add(queryExpression);
types.add(type[0]);
}
// Create the COALESCE expression
queryExpression = queryContext.getBaseExpression();
queryExpression = queryExpression.coalesce(expressions);
// Set the expression type
type[0] = queryContext.typeResolver().compareCollectionEquivalentTypes(types);
}
I removed the section where it tries to guess parameters types, because:
- In both cases it was implemented incorrectly (in 2.7.4 it was doing nothing, in 2.7.5 it was assigning the wrong type)
- You can't guess a correct type. Even if the type of one of the parameters is known (like
c.iso2
in my example), you still cannot use this type, because the field could use a converter, so the parameter type for another argument would be different.
Workaround for those who needs to use the newer version of EL -- flip the expression so that the previous type is the correct type.
String jsql = "select c from Country c where c.name is not null and 'DE' != coalesce(c.iso2, :p1)";
I also reported the bug here https://bugs.eclipse.org/bugs/show_bug.cgi?id=575107