From 6c48ae12620bf5cb2093311b84c6353f9c936296 Mon Sep 17 00:00:00 2001 From: clayly Date: Thu, 9 Apr 2026 16:20:36 +0300 Subject: [PATCH 1/2] 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. --- .../hawkbit/ql/jpa/SpecificationBuilder.java | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilder.java b/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilder.java index 888807316f..5aa4a22e5f 100644 --- a/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilder.java +++ b/hawkbit-ql-jpa/src/main/java/org/eclipse/hawkbit/ql/jpa/SpecificationBuilder.java @@ -31,7 +31,6 @@ import java.util.stream.Collectors; import jakarta.annotation.Nonnull; -import jakarta.persistence.PersistenceException; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Expression; @@ -373,41 +372,29 @@ private Predicate notEqual(final Path fieldPath, Object value) { } } - @SuppressWarnings("java:S1872") // java:S1872 - sometimes class could be unavailable at runtime private Predicate like(final Path fieldPath, final String sqlValue) { - try { - if (caseWise(fieldPath)) { - return cb.like(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR); - } else { - return cb.like(fieldPath, sqlValue, ESCAPE_CHAR); - } - } catch (final PersistenceException e) { - if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class && - "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.isNotNull(); - } else { - throw e; - } + // LIKE on non-String fields (e.g. bigint) with wildcard-only value is equivalent to IS NOT NULL. + // Must be checked before building the SQL predicate because some databases (PostgreSQL, YugabyteDB) + // reject LIKE on non-text columns at the SQL level, before any JPA-provider-level validation. + if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class) { + return fieldPath.isNotNull(); + } + if (caseWise(fieldPath)) { + return cb.like(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR); + } else { + return cb.like(fieldPath, sqlValue, ESCAPE_CHAR); } } - @SuppressWarnings("java:S1872") // java:S1872 - sometimes class could be unavailable at runtime private Predicate notLike(final Path fieldPath, final String sqlValue) { - try { - if (caseWise(fieldPath)) { - return cb.notLike(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR); - } else { - return cb.notLike(fieldPath, sqlValue, ESCAPE_CHAR); - } - } catch (final PersistenceException e) { - if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class && - "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; - } + // NOT LIKE on non-String fields with wildcard-only value is equivalent to IS NULL. + if ("%".equals(sqlValue) && fieldPath.getJavaType() != String.class) { + return fieldPath.isNull(); + } + if (caseWise(fieldPath)) { + return cb.notLike(cb.upper(fieldPath), sqlValue.toUpperCase(), ESCAPE_CHAR); + } else { + return cb.notLike(fieldPath, sqlValue, ESCAPE_CHAR); } } From f6801ed1919b2869841787d01a36d3a99835f807 Mon Sep 17 00:00:00 2001 From: clayly Date: Thu, 9 Apr 2026 16:39:12 +0300 Subject: [PATCH 2/2] Trigger ECA re-check