Fix LIKE on non-String fields failing on PostgreSQL (#3008)
* Fix LIKE on non-String fields failing on PostgreSQL-compatible databases The like() and notLike() methods in SpecificationBuilder relied on catching a Hibernate-specific CoercionException when LIKE was applied to non-String fields (e.g. bigint) with a wildcard-only value. However, with EclipseLink the invalid SQL is sent directly to the database, where PostgreSQL and compatible databases (YugabyteDB, CockroachDB) reject it with "operator does not exist: bigint ~~ text". Move the non-String field check before building the SQL predicate, making it database-agnostic and JPA-provider-agnostic. A wildcard-only LIKE on a non-String field is semantically equivalent to IS NOT NULL (and NOT LIKE to IS NULL), which is what the fallback already produced. * Trigger ECA re-check
This commit is contained in:
@@ -31,7 +31,6 @@ import java.util.Objects;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import jakarta.persistence.PersistenceException;
|
|
||||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
import jakarta.persistence.criteria.CriteriaQuery;
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
import jakarta.persistence.criteria.Expression;
|
import jakarta.persistence.criteria.Expression;
|
||||||
@@ -377,41 +376,29 @@ public class SpecificationBuilder<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("java:S1872") // java:S1872 - sometimes class could be unavailable at runtime
|
|
||||||
private Predicate like(final Path<String> fieldPath, final String sqlValue) {
|
private Predicate like(final Path<String> fieldPath, final String sqlValue) {
|
||||||
try {
|
// LIKE on non-String fields (e.g. bigint) with wildcard-only value is equivalent to IS NOT NULL.
|
||||||
if (caseWise(fieldPath)) {
|
// Must be checked before building the SQL predicate because some databases (PostgreSQL, YugabyteDB)
|
||||||
return cb.like(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR);
|
// reject LIKE on non-text columns at the SQL level, before any JPA-provider-level validation.
|
||||||
} else {
|
if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class) {
|
||||||
return cb.like(fieldPath, sqlValue, ESCAPE_CHAR);
|
return fieldPath.isNotNull();
|
||||||
}
|
}
|
||||||
} catch (final PersistenceException e) {
|
if (caseWise(fieldPath)) {
|
||||||
if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class &&
|
return cb.like(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR);
|
||||||
"org.hibernate.type.descriptor.java.CoercionException".equals(e.getClass().getName())) {
|
} else {
|
||||||
// hibernate throws an exception if we try to do == on non-string field with wildcard only
|
return cb.like(fieldPath, sqlValue, ESCAPE_CHAR);
|
||||||
return fieldPath.isNotNull();
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("java:S1872") // java:S1872 - sometimes class could be unavailable at runtime
|
|
||||||
private Predicate notLike(final Path<String> fieldPath, final String sqlValue) {
|
private Predicate notLike(final Path<String> fieldPath, final String sqlValue) {
|
||||||
try {
|
// NOT LIKE on non-String fields with wildcard-only value is equivalent to IS NULL.
|
||||||
if (caseWise(fieldPath)) {
|
if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class) {
|
||||||
return cb.notLike(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR);
|
return fieldPath.isNull();
|
||||||
} else {
|
}
|
||||||
return cb.notLike(fieldPath, sqlValue, ESCAPE_CHAR);
|
if (caseWise(fieldPath)) {
|
||||||
}
|
return cb.notLike(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR);
|
||||||
} catch (final PersistenceException e) {
|
} else {
|
||||||
if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class &&
|
return cb.notLike(fieldPath, sqlValue, ESCAPE_CHAR);
|
||||||
"org.hibernate.type.descriptor.java.CoercionException".equals(e.getClass().getName())) {
|
|
||||||
// hibernate throws an exception if we try to do == on non-string field with wildcard only
|
|
||||||
return fieldPath.isNull();
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user