diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java index fb72fc71f49f..f5955adf0737 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java @@ -86,10 +86,15 @@ protected String substituteBrackets(String sqlQuery) throws QueryException { final char ch = sqlQuery.charAt( index ); switch (ch) { case '\'': - if (!doubleQuoted && !escaped) { - singleQuoted = !singleQuoted; + if (escaped) { + token.append(ch); + } + else { + if (!doubleQuoted) { + singleQuoted = !singleQuoted; + } + result.append(ch); } - result.append(ch); break; case '"': if (!singleQuoted && !escaped) { @@ -220,7 +225,7 @@ private String resolveCollectionProperties( } aliasesFound++; return collectionPersister.selectFragment( aliasName, collectionSuffix ) - + ", " + resolveProperties( aliasName, propertyName ); + + ", " + resolveProperties( aliasName, propertyName ); case "element.*": return resolveProperties( aliasName, "*" ); default: @@ -270,7 +275,7 @@ private void validate(String aliasName, String propertyName, String[] columnAlia // TODO: better error message since we actually support composites if names are explicitly listed throw new QueryException( "SQL queries only support properties mapped to a single column - property [" + - propertyName + "] is mapped to " + columnAliases.length + " columns.", + propertyName + "] is mapped to " + columnAliases.length + " columns.", originalQueryString ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/SQLQueryParserUnitTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/SQLQueryParserUnitTests.java index 2e57b17ed242..be06363bef6e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/sql/SQLQueryParserUnitTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/sql/SQLQueryParserUnitTests.java @@ -13,6 +13,8 @@ import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; @@ -24,6 +26,35 @@ @SuppressWarnings("JUnitMalformedDeclaration") public class SQLQueryParserUnitTests { + @ParameterizedTest + @DomainModel + @SessionFactory + @RequiresDialect(H2Dialect.class) + @ValueSource(strings = { + "{d '2025-06-18'}", + "{t '14:00'}", + "{t '14:00:00'}", + "{ts '2025-06-18T14:00'}", + "{ts '2025-06-18T14:00:00'}", + "{ts '2025-06-18T14:00:00.123'}", + "{ts '2025-06-18T14:00:00+01:00'}"}) + void testJDBCEscapeSyntaxParsing(String variant, SessionFactoryScope scope) { + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + final String sqlQuery = "select id, name from {h-domain}the_table where date = " + variant; + + final String full = processSqlString( sqlQuery, "my_catalog", "my_schema", sessionFactory ); + assertThat( full ).contains( variant ); + + final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory ); + assertThat( catalogOnly ).contains( variant ); + + final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory ); + assertThat( schemaOnly ).contains( variant ); + + final String none = processSqlString( sqlQuery, null, null, sessionFactory ); + assertThat( none ).contains( variant ); + } + @Test @DomainModel @SessionFactory