HHH-18256 Don't prefix literal type prefix in SQL fragments

This commit is contained in:
Christian Beikov 2024-06-11 13:20:09 +02:00
parent 00977fe9a9
commit 0663f811ff
2 changed files with 131 additions and 0 deletions

View File

@ -31,6 +31,7 @@ public final class Template {
private static final Set<String> KEYWORDS = new HashSet<>();
private static final Set<String> BEFORE_TABLE_KEYWORDS = new HashSet<>();
private static final Set<String> FUNCTION_KEYWORDS = new HashSet<>();
private static final Set<String> LITERAL_PREFIXES = new HashSet<>();
public static final String PUNCTUATION = "=><!+-*/()',|&`";
static {
@ -82,6 +83,15 @@ public final class Template {
FUNCTION_KEYWORDS.add("then");
FUNCTION_KEYWORDS.add("else");
FUNCTION_KEYWORDS.add("end");
LITERAL_PREFIXES.add("n");
LITERAL_PREFIXES.add("x");
LITERAL_PREFIXES.add("varbyte");
LITERAL_PREFIXES.add("bx");
LITERAL_PREFIXES.add("bytea");
LITERAL_PREFIXES.add("date");
LITERAL_PREFIXES.add("time");
LITERAL_PREFIXES.add("timestamp");
}
public static final String TEMPLATE = "$PlaceHolder$";
@ -177,6 +187,39 @@ public final class Template {
isQuoteCharacter = true;
isOpenQuote = false;
}
else if ( LITERAL_PREFIXES.contains( lcToken ) ) {
if ( "'".equals( nextToken ) ) {
// Don't prefix a literal
result.append( token );
continue;
}
else if ( nextToken != null && Character.isWhitespace( nextToken.charAt( 0 ) ) ) {
final StringBuilder additionalTokens = new StringBuilder();
TimeZoneTokens possibleNextToken = null;
do {
possibleNextToken = possibleNextToken == null
? 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 ) ) {
// Don't prefix a literal
result.append( token );
result.append( additionalTokens );
continue;
}
else {
isOpenQuote = false;
}
}
else {
isOpenQuote = false;
}
}
else {
isOpenQuote = false;
}
@ -358,6 +401,39 @@ public final class Template {
return result.toString();
}
private enum TimeZoneTokens {
NONE,
WITH,
TIME,
ZONE;
static TimeZoneTokens getPossibleNextTokens(String lctoken) {
switch ( lctoken ) {
case "time":
case "timestamp":
return WITH;
default:
return NONE;
}
}
public TimeZoneTokens nextToken() {
if ( this == WITH ) {
return TIME;
}
else if ( this == TIME ) {
return ZONE;
}
else {
return NONE;
}
}
public boolean isToken(String token) {
return this != NONE && name().equalsIgnoreCase( token );
}
}
public static List<String> collectColumnNames(
String sql,
Dialect dialect,

View File

@ -0,0 +1,55 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.sql;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.sql.Template;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.testing.orm.domain.StandardDomainModel;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SessionFactory
@DomainModel(standardModels = StandardDomainModel.GAMBIT)
public class TemplateTest {
@Test
@JiraKey("HHH-18256")
public void templateLiterals(SessionFactoryScope scope) {
assertWhereStringTemplate( "N'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "X'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "BX'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "VARBYTE'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "bytea 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "bytea 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "date 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "time 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "timestamp 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "timestamp with time zone 'a'", scope.getSessionFactory() );
assertWhereStringTemplate( "time with time zone 'a'", scope.getSessionFactory() );
}
private static void assertWhereStringTemplate(String sql, SessionFactoryImplementor sf) {
final String template = Template.renderWhereStringTemplate(
sql,
sf.getJdbcServices().getDialect(),
sf.getTypeConfiguration(),
sf.getQueryEngine().getSqmFunctionRegistry()
);
assertEquals( sql, template );
}
}