From fbf70fef44c2694b45b49bc44ad71c171dbc254d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Cedomir=20Igaly?= Date: Sat, 18 Jan 2025 13:23:28 +0100 Subject: [PATCH] HHH-19052 Using lookahead StringTokenizer when analyzing part of SQL query prefixed by literal prefix --- .../main/java/org/hibernate/sql/Template.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Template.java b/hibernate-core/src/main/java/org/hibernate/sql/Template.java index 05c5a78d028c..dc193c5d4e43 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Template.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Template.java @@ -194,6 +194,8 @@ else if ( LITERAL_PREFIXES.contains( lcToken ) ) { continue; } else if ( nextToken != null && Character.isWhitespace( nextToken.charAt( 0 ) ) ) { + final StringTokenizer lookahead = lookahead( sqlWhereString, symbols, tokens); + String lookaheadToken = lookahead.hasMoreTokens() ? lookahead.nextToken() : null; final StringBuilder additionalTokens = new StringBuilder(); TimeZoneTokens possibleNextToken = null; do { @@ -201,15 +203,18 @@ else if ( nextToken != null && Character.isWhitespace( nextToken.charAt( 0 ) ) ) ? TimeZoneTokens.getPossibleNextTokens( lcToken ) : possibleNextToken.nextToken(); do { - additionalTokens.append( nextToken ); - hasMore = tokens.hasMoreTokens(); - nextToken = tokens.nextToken(); - } while ( nextToken != null && Character.isWhitespace( nextToken.charAt( 0 ) ) ); - } while ( nextToken != null && possibleNextToken.isToken( nextToken ) ); - if ( "'".equals( nextToken ) ) { + additionalTokens.append( lookaheadToken ); + lookaheadToken = lookahead.hasMoreTokens() ? lookahead.nextToken() : null; + } while ( lookaheadToken != null && Character.isWhitespace( lookaheadToken.charAt( 0 ) ) ); + } while ( lookaheadToken != null && possibleNextToken.isToken( lookaheadToken ) ); + if ( "'".equals( lookaheadToken ) ) { // Don't prefix a literal result.append( token ); result.append( additionalTokens ); + while (tokens.countTokens() > lookahead.countTokens()) { + hasMore = tokens.hasMoreTokens(); + nextToken = hasMore ? tokens.nextToken() : null; + } continue; } else { @@ -401,6 +406,24 @@ else if ( inFromClause && ",".equals(lcToken) ) { return result.toString(); } + /** + * Clone the given token stream, returning a token stream which begins + * from the next token. + * + * @param sql the full SQL we are scanning + * @param symbols the delimiter symbols + * @param tokens the current token stream + * @return a cloned token stream + */ + private static StringTokenizer lookahead(String sql, String symbols, StringTokenizer tokens) { + final StringTokenizer lookahead = + new StringTokenizer( sql, symbols, true ); + while ( lookahead.countTokens() > tokens.countTokens() + 1) { + lookahead.nextToken(); + } + return lookahead; + } + private enum TimeZoneTokens { NONE, WITH,