From 22559a9f43e13969f68ecd76a8e7a35152eef7d1 Mon Sep 17 00:00:00 2001 From: Stefan Trienen Date: Wed, 18 Jun 2025 15:51:58 +0200 Subject: [PATCH 1/3] HHH-19558: Fixed support of JDBC escape syntax --- .../query/sql/internal/SQLQueryParser.java | 12 +++++++++-- .../orm/test/sql/SQLQueryParserUnitTests.java | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) 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..e518eb6c504c 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 @@ -89,13 +89,21 @@ protected String substituteBrackets(String sqlQuery) throws QueryException { if (!doubleQuoted && !escaped) { singleQuoted = !singleQuoted; } - result.append(ch); + if (escaped) { + token.append(ch); + } else { + result.append(ch); + } break; case '"': if (!singleQuoted && !escaped) { doubleQuoted = !doubleQuoted; } - result.append(ch); + if (escaped) { + token.append(ch); + } else { + result.append(ch); + } break; case '{': if (!singleQuoted && !doubleQuoted) { 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..f7f70175d5da 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 @@ -24,6 +24,27 @@ @SuppressWarnings("JUnitMalformedDeclaration") public class SQLQueryParserUnitTests { + @Test + @DomainModel + @SessionFactory + @RequiresDialect(H2Dialect.class) + void testJDBCEscapeSyntaxParsing(SessionFactoryScope scope) { + final SessionFactoryImplementor sessionFactory = scope.getSessionFactory(); + final String sqlQuery = "select id, name from {h-domain}the_table where date = {d '2025-06-18'}"; + + final String full = processSqlString( sqlQuery, "my_catalog", "my_schema", sessionFactory ); + assertThat( full ).contains( "{d '2025-06-18'}" ); + + final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory ); + assertThat( catalogOnly ).contains( "{d '2025-06-18'}" ); + + final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory ); + assertThat( schemaOnly ).contains( "{d '2025-06-18'}" ); + + final String none = processSqlString( sqlQuery, null, null, sessionFactory ); + assertThat( none ).contains( "{d '2025-06-18'}" ); + } + @Test @DomainModel @SessionFactory From 4d8019de356eb02d093b67ef8df810c9642c0294 Mon Sep 17 00:00:00 2001 From: Stefan Trienen Date: Wed, 18 Jun 2025 16:48:23 +0200 Subject: [PATCH 2/3] HHH-19558: Improved Test --- .../orm/test/sql/SQLQueryParserUnitTests.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) 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 f7f70175d5da..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,25 +26,33 @@ @SuppressWarnings("JUnitMalformedDeclaration") public class SQLQueryParserUnitTests { - @Test + @ParameterizedTest @DomainModel @SessionFactory @RequiresDialect(H2Dialect.class) - void testJDBCEscapeSyntaxParsing(SessionFactoryScope scope) { + @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 = {d '2025-06-18'}"; + 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( "{d '2025-06-18'}" ); + assertThat( full ).contains( variant ); final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory ); - assertThat( catalogOnly ).contains( "{d '2025-06-18'}" ); + assertThat( catalogOnly ).contains( variant ); final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory ); - assertThat( schemaOnly ).contains( "{d '2025-06-18'}" ); + assertThat( schemaOnly ).contains( variant ); final String none = processSqlString( sqlQuery, null, null, sessionFactory ); - assertThat( none ).contains( "{d '2025-06-18'}" ); + assertThat( none ).contains( variant ); } @Test From ee0b8ff97d2e8ef782922c48a867caa5fa150e9e Mon Sep 17 00:00:00 2001 From: Stefan Trienen Date: Mon, 30 Jun 2025 09:35:00 +0200 Subject: [PATCH 3/3] HHH-19558: Removed double quoted case --- .../query/sql/internal/SQLQueryParser.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) 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 e518eb6c504c..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,12 +86,13 @@ 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 { + } + else { + if (!doubleQuoted) { + singleQuoted = !singleQuoted; + } result.append(ch); } break; @@ -99,11 +100,7 @@ protected String substituteBrackets(String sqlQuery) throws QueryException { if (!singleQuoted && !escaped) { doubleQuoted = !doubleQuoted; } - if (escaped) { - token.append(ch); - } else { - result.append(ch); - } + result.append(ch); break; case '{': if (!singleQuoted && !doubleQuoted) { @@ -228,7 +225,7 @@ private String resolveCollectionProperties( } aliasesFound++; return collectionPersister.selectFragment( aliasName, collectionSuffix ) - + ", " + resolveProperties( aliasName, propertyName ); + + ", " + resolveProperties( aliasName, propertyName ); case "element.*": return resolveProperties( aliasName, "*" ); default: @@ -278,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 ); }