From ff50a1bb145910904e027ab08bebaf6fd151557f Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Tue, 29 Jul 2025 12:45:41 +0200 Subject: [PATCH] HHH-18956 Fix native query brace replacement dollar quoted literal support --- .../query/sql/internal/SQLQueryParser.java | 11 ++- .../PostgisDollarQuoteNativeQueryTest.java | 90 +++++++++++++++++++ 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/postgis/PostgisDollarQuoteNativeQueryTest.java 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 f5955adf0737..052fe5558359 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 @@ -97,10 +97,15 @@ protected String substituteBrackets(String sqlQuery) throws QueryException { } break; case '"': - if (!singleQuoted && !escaped) { - doubleQuoted = !doubleQuoted; + if (escaped) { + token.append(ch); + } + else { + if ( !singleQuoted ) { + doubleQuoted = !doubleQuoted; + } + result.append( ch ); } - result.append(ch); break; case '{': if (!singleQuoted && !doubleQuoted) { diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/postgis/PostgisDollarQuoteNativeQueryTest.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/postgis/PostgisDollarQuoteNativeQueryTest.java new file mode 100644 index 000000000000..0bd44bcebaba --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/postgis/PostgisDollarQuoteNativeQueryTest.java @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.spatial.dialect.postgis; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.geolatte.geom.G2D; +import org.geolatte.geom.Point; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.type.SqlTypes; +import org.junit.jupiter.api.Test; + + +import static org.geolatte.geom.builder.DSL.g; +import static org.geolatte.geom.builder.DSL.point; +import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DomainModel(annotatedClasses = PostgisDollarQuoteNativeQueryTest.Location.class ) +@SessionFactory +@RequiresDialect(PostgreSQLDialect.class) +@Jira( "https://hibernate.atlassian.net/browse/HHH-18956" ) +class PostgisDollarQuoteNativeQueryTest { + + private static final Long LOCATION_ID = 123412L; + private static final String DOLLAR_QUOTE = "$asdas$"; + + @Test + void test(SessionFactoryScope scope) { + scope.inTransaction( s -> { + final Point point = point( WGS84, g( 30.5, 50.4 ) ); + //noinspection SqlSourceToSinkFlow + s.createNativeMutationQuery( + String.format( + "INSERT INTO location (id, point) " + + "VALUES (%s, %s) " + + "ON CONFLICT DO NOTHING;", + DOLLAR_QUOTE + LOCATION_ID + DOLLAR_QUOTE, + String.format( + "ST_SetSRID(ST_GeomFromGeoJSON(%s%s%s), 4326)", + DOLLAR_QUOTE, + toJsonString( point ), + DOLLAR_QUOTE + ) + ) + ).executeUpdate(); + + final Location location = s.find( Location.class, LOCATION_ID ); + assertEquals( point, location.point ); + } ); + } + + private static String toJsonString(Point point) { + return "{\"type\":\"" + point.getGeometryType().getCamelCased() + "\",\"coordinates\":" + point.getPosition() + "}"; + } + + @Entity(name = "Location") + public static class Location { + + @Id + Long id; + + @JdbcTypeCode(SqlTypes.GEOMETRY) + Point point; + + public Location() { + } + + public Location(Long id, Point point) { + this.id = id; + this.point = point; + } + + @Override + public String toString() { + return "Location{" + + "id=" + id + + ", point=" + point + + '}'; + } + } +}