HHH-17435 Allow input param as trim character and fix single quote
This commit is contained in:
parent
2b025e9b4e
commit
2fce965730
|
@ -138,23 +138,23 @@ public class AltibaseDialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
switch ( specification ) {
|
||||
case BOTH:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "trim(?1)"
|
||||
: "trim(?1, '" + character + "')";
|
||||
: "trim(?1,?2)";
|
||||
case LEADING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "ltrim(?1)"
|
||||
: "ltrim(?1,'" + character + "')";
|
||||
: "ltrim(?1,?2)";
|
||||
case TRAILING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "rtrim(?1)"
|
||||
: "rtrim(?1,'" + character + "')";
|
||||
: "rtrim(?1,?2)";
|
||||
}
|
||||
|
||||
return super.trimPattern( specification, character );
|
||||
return super.trimPattern( specification, isWhitespace );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.hibernate.dialect.function.CountFunction;
|
|||
import org.hibernate.dialect.function.DB2FormatEmulation;
|
||||
import org.hibernate.dialect.function.DB2PositionFunction;
|
||||
import org.hibernate.dialect.function.DB2SubstringFunction;
|
||||
import org.hibernate.dialect.function.TrimFunction;
|
||||
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.DB2LimitHandler;
|
||||
|
@ -403,6 +404,13 @@ public class DB2LegacyDialect extends Dialect {
|
|||
.setArgumentListSignature("(STRING string, STRING pattern)")
|
||||
.register();
|
||||
|
||||
//trim() requires trim characters to be constant literals
|
||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||
this,
|
||||
functionContributions.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.INLINE_PARAMETERS
|
||||
) );
|
||||
|
||||
functionFactory.windowFunctions();
|
||||
if ( getDB2Version().isSameOrAfter( 9, 5 ) ) {
|
||||
functionFactory.listagg( null );
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.StaleObjectStateException;
|
|||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.dialect.*;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.TrimFunction;
|
||||
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.lock.LockingStrategy;
|
||||
|
@ -268,6 +269,13 @@ public class HSQLLegacyDialect extends Dialect {
|
|||
functionFactory.arrayTrim_trim_array();
|
||||
functionFactory.arrayFill_hsql();
|
||||
functionFactory.arrayToString_hsql();
|
||||
|
||||
//trim() requires parameters to be cast when used as trim character
|
||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||
this,
|
||||
functionContributions.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER
|
||||
) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -203,8 +203,8 @@ public class MaxDBDialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, character);
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, isWhitespace );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -431,7 +431,7 @@ public class RDMSOS2200Dialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, character);
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
return AbstractTransactSQLDialect.replaceLtrimRtrim( specification, isWhitespace );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,25 +390,25 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
switch ( specification ) {
|
||||
case BOTH:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "trim(?1)"
|
||||
: "trim('" + character + "' from ?1)";
|
||||
: "trim(?2 from ?1)";
|
||||
case LEADING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "ltrim(?1)"
|
||||
: "ltrim(?1,'" + character + "')";
|
||||
: "ltrim(?1,?2)";
|
||||
case TRAILING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "rtrim(?1)"
|
||||
: "rtrim(?1,'" + character + "')";
|
||||
: "rtrim(?1,?2)";
|
||||
}
|
||||
throw new UnsupportedOperationException( "Unsupported specification: " + specification );
|
||||
}
|
||||
return super.trimPattern( specification, character );
|
||||
return super.trimPattern( specification, isWhitespace );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -353,20 +353,20 @@ public class SQLiteDialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
switch ( specification ) {
|
||||
case BOTH:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "trim(?1)"
|
||||
: "trim(?1,'" + character + "')";
|
||||
: "trim(?1,?2)";
|
||||
case LEADING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "ltrim(?1)"
|
||||
: "ltrim(?1,'" + character + "')";
|
||||
: "ltrim(?1,?2)";
|
||||
case TRAILING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "rtrim(?1)"
|
||||
: "rtrim(?1,'" + character + "')";
|
||||
: "rtrim(?1,?2)";
|
||||
}
|
||||
throw new UnsupportedOperationException( "Unsupported specification: " + specification );
|
||||
}
|
||||
|
|
|
@ -425,12 +425,6 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
|
|||
return "datediff(?1,?2,?3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return super.trimPattern(specification, character)
|
||||
.replace("replace", "str_replace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||
throw new UnsupportedOperationException( "format() function not supported on Sybase");
|
||||
|
|
|
@ -1369,6 +1369,7 @@ trimSpecification
|
|||
|
||||
trimCharacter
|
||||
: STRING_LITERAL
|
||||
| parameter
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
|
@ -166,28 +166,32 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return replaceLtrimRtrim(specification, character);
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
return replaceLtrimRtrim( specification, isWhitespace );
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #replaceLtrimRtrim(TrimSpec, boolean)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
public static String replaceLtrimRtrim(TrimSpec specification, char character) {
|
||||
boolean blank = character == ' ';
|
||||
return replaceLtrimRtrim( specification, character == ' ' );
|
||||
}
|
||||
|
||||
public static String replaceLtrimRtrim(TrimSpec specification, boolean isWhitespace) {
|
||||
switch ( specification ) {
|
||||
case LEADING:
|
||||
return blank
|
||||
return isWhitespace
|
||||
? "ltrim(?1)"
|
||||
: "replace(replace(ltrim(replace(replace(?1,' ','#%#%'),'@',' ')),' ','@'),'#%#%',' ')"
|
||||
.replace('@', character);
|
||||
: "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)";
|
||||
case TRAILING:
|
||||
return blank
|
||||
return isWhitespace
|
||||
? "rtrim(?1)"
|
||||
: "replace(replace(rtrim(replace(replace(?1,' ','#%#%'),'@',' ')),' ','@'),'#%#%',' ')"
|
||||
.replace('@', character);
|
||||
: "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)";
|
||||
default:
|
||||
return blank
|
||||
return isWhitespace
|
||||
? "ltrim(rtrim(?1))"
|
||||
: "replace(replace(ltrim(rtrim(replace(replace(?1,' ','#%#%'),'@',' '))),' ','@'),'#%#%',' ')"
|
||||
.replace('@', character);
|
||||
: "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.dialect.function.CountFunction;
|
|||
import org.hibernate.dialect.function.DB2FormatEmulation;
|
||||
import org.hibernate.dialect.function.DB2PositionFunction;
|
||||
import org.hibernate.dialect.function.DB2SubstringFunction;
|
||||
import org.hibernate.dialect.function.TrimFunction;
|
||||
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.DB2LimitHandler;
|
||||
|
@ -391,6 +392,13 @@ public class DB2Dialect extends Dialect {
|
|||
.setArgumentListSignature("(STRING string, STRING pattern)")
|
||||
.register();
|
||||
|
||||
//trim() requires trim characters to be constant literals
|
||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||
this,
|
||||
functionContributions.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.INLINE_PARAMETERS
|
||||
) );
|
||||
|
||||
functionFactory.windowFunctions();
|
||||
functionFactory.listagg( null );
|
||||
}
|
||||
|
|
|
@ -1484,11 +1484,29 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
*
|
||||
* @param specification {@code leading} or {@code trailing}
|
||||
* @param character the character to trim
|
||||
*
|
||||
* @deprecated Use {@link #trimPattern(TrimSpec, boolean)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return character == ' '
|
||||
? "trim(" + specification + " from ?1)"
|
||||
: "trim(" + specification + " '" + character + "' from ?1)";
|
||||
return trimPattern( specification, character == ' ' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a pattern for the SQL equivalent to a
|
||||
* {@code trim()} function call. The resulting
|
||||
* pattern must contain a ?1 placeholder for the
|
||||
* argument of type {@link String} and a ?2 placeholder
|
||||
* for the trim character if {@code isWhitespace}
|
||||
* was false.
|
||||
*
|
||||
* @param specification {@linkplain TrimSpec#LEADING leading}, {@linkplain TrimSpec#TRAILING trailing}
|
||||
* or {@linkplain TrimSpec#BOTH both}
|
||||
* @param isWhitespace {@code true} if the trim character is a whitespace and can be omitted,
|
||||
* {@code false} if it must be explicit and a ?2 placeholder should be included in the pattern
|
||||
*/
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
return "trim(" + specification + ( isWhitespace ? "" : " ?2" ) + " from ?1)";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -290,6 +290,11 @@ public class DialectDelegateWrapper extends Dialect {
|
|||
return wrapped.trimPattern( specification, character );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
return wrapped.trimPattern( specification, isWhitespace );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFractionalTimestampArithmetic() {
|
||||
return wrapped.supportsFractionalTimestampArithmetic();
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.sql.Types;
|
|||
|
||||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.TrimFunction;
|
||||
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
|
@ -208,6 +209,13 @@ public class HSQLDialect extends Dialect {
|
|||
functionFactory.arrayTrim_trim_array();
|
||||
functionFactory.arrayFill_hsql();
|
||||
functionFactory.arrayToString_hsql();
|
||||
|
||||
//trim() requires parameters to be cast when used as trim character
|
||||
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
|
||||
this,
|
||||
functionContributions.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER
|
||||
) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -399,25 +399,25 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
public String trimPattern(TrimSpec specification, boolean isWhitespace) {
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
switch ( specification ) {
|
||||
case BOTH:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "trim(?1)"
|
||||
: "trim('" + character + "' from ?1)";
|
||||
: "trim(?2 from ?1)";
|
||||
case LEADING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "ltrim(?1)"
|
||||
: "ltrim(?1,'" + character + "')";
|
||||
: "ltrim(?1,?2)";
|
||||
case TRAILING:
|
||||
return character == ' '
|
||||
return isWhitespace
|
||||
? "rtrim(?1)"
|
||||
: "rtrim(?1,'" + character + "')";
|
||||
: "rtrim(?1,?2)";
|
||||
}
|
||||
throw new UnsupportedOperationException( "Unsupported specification: " + specification );
|
||||
}
|
||||
return super.trimPattern( specification, character );
|
||||
return super.trimPattern( specification, isWhitespace );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -447,12 +447,6 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
return "datediff(?1,?2,?3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return super.trimPattern(specification, character)
|
||||
.replace("replace", "str_replace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||
throw new UnsupportedOperationException( "format() function not supported on Sybase");
|
||||
|
|
|
@ -6,44 +6,56 @@
|
|||
*/
|
||||
package org.hibernate.dialect.function;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.query.sqm.TrimSpec;
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
|
||||
import org.hibernate.query.sqm.produce.function.FunctionArgumentException;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
|
||||
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.TrimSpecification;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TRIM_SPEC;
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TRIM_SPEC;
|
||||
|
||||
/**
|
||||
* ANSI SQL-standard {@code trim()} function, which has a funny syntax
|
||||
* involving a {@link TrimSpec}, and portability is achieved using
|
||||
* {@link Dialect#trimPattern(TrimSpec, char)}.
|
||||
* {@link Dialect#trimPattern(TrimSpec, boolean)}.
|
||||
* <p>
|
||||
* For example, {@code trim(leading ' ' from text)}.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class TrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
private final Dialect dialect;
|
||||
private SqlAstNodeRenderingMode argumentRenderingMode;
|
||||
|
||||
public TrimFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
|
||||
this( dialect, typeConfiguration, SqlAstNodeRenderingMode.DEFAULT );
|
||||
}
|
||||
|
||||
public TrimFunction(
|
||||
Dialect dialect,
|
||||
TypeConfiguration typeConfiguration,
|
||||
SqlAstNodeRenderingMode argumentRenderingMode) {
|
||||
super(
|
||||
"trim",
|
||||
new ArgumentTypesValidator(
|
||||
|
@ -56,6 +68,7 @@ public class TrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
|||
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, TRIM_SPEC, STRING, STRING )
|
||||
);
|
||||
this.dialect = dialect;
|
||||
this.argumentRenderingMode = argumentRenderingMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,12 +78,36 @@ public class TrimFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
|||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> walker) {
|
||||
final TrimSpec specification = ( (TrimSpecification) sqlAstArguments.get( 0 ) ).getSpecification();
|
||||
final Object trimCharacter = ( (Literal) sqlAstArguments.get( 1 ) ).getLiteralValue();
|
||||
final SqlAstNode trimCharacter = sqlAstArguments.get( 1 );
|
||||
final boolean isWhitespace = isWhitespace( trimCharacter );
|
||||
final Expression sourceExpr = (Expression) sqlAstArguments.get( 2 );
|
||||
|
||||
String trim = dialect.trimPattern( specification, (char) trimCharacter );
|
||||
final String trim = dialect.trimPattern( specification, isWhitespace );
|
||||
|
||||
new PatternRenderer( trim ).render( sqlAppender, Collections.singletonList( sourceExpr ), walker );
|
||||
final List<? extends SqlAstNode> args = isWhitespace ?
|
||||
Collections.singletonList( sourceExpr ) :
|
||||
List.of( sourceExpr, trimCharacter );
|
||||
new PatternRenderer( trim, argumentRenderingMode ).render( sqlAppender, args, walker );
|
||||
}
|
||||
|
||||
private static boolean isWhitespace(SqlAstNode trimCharacter) {
|
||||
if ( trimCharacter instanceof Literal ) {
|
||||
final char literalValue = (char) ( (Literal) trimCharacter ).getLiteralValue();
|
||||
return literalValue == ' ';
|
||||
}
|
||||
else {
|
||||
assert trimCharacter instanceof SqmParameterInterpretation;
|
||||
final JdbcType jdbcType = ( (SqmParameterInterpretation) trimCharacter ).getExpressionType()
|
||||
.getSingleJdbcMapping()
|
||||
.getJdbcType();
|
||||
if ( jdbcType.getJdbcTypeCode() != SqlTypes.CHAR ) {
|
||||
throw new FunctionArgumentException( String.format(
|
||||
"Expected parameter used as trim character to be Character typed, instead was [%s]",
|
||||
jdbcType.getFriendlyName()
|
||||
) );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
|
|
|
@ -4782,7 +4782,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
public SqmExpression<?> visitTrimFunction(HqlParser.TrimFunctionContext ctx) {
|
||||
final SqmExpression<?> source = (SqmExpression<?>) ctx.expression().accept( this );
|
||||
final SqmTrimSpecification trimSpec = visitTrimSpecification( ctx.trimSpecification() );;
|
||||
final SqmLiteral<Character> trimChar = visitTrimCharacter( ctx.trimCharacter() );
|
||||
final SqmExpression<Character> trimChar = visitTrimCharacter( ctx.trimCharacter() );
|
||||
|
||||
return getFunctionDescriptor("trim").generateSqmExpression(
|
||||
asList(
|
||||
|
@ -4816,15 +4816,25 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
|
||||
@Override
|
||||
public SqmLiteral<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
|
||||
final String trimCharText = ctx != null
|
||||
? unquoteStringLiteral( ctx.getText() )
|
||||
: " "; // JPA says space is the default
|
||||
|
||||
if ( trimCharText.length() != 1 ) {
|
||||
throw new SemanticException( "Trim character for trim() function must be single character, found '" + trimCharText + "'" );
|
||||
public SqmExpression<Character> visitTrimCharacter(HqlParser.TrimCharacterContext ctx) {
|
||||
final String trimCharText;
|
||||
if ( ctx == null ) {
|
||||
// JPA says space is the default
|
||||
trimCharText = " ";
|
||||
}
|
||||
else {
|
||||
final ParseTree child = ctx.getChild( 0 );
|
||||
if ( child instanceof HqlParser.ParameterContext ) {
|
||||
//noinspection unchecked
|
||||
return (SqmExpression<Character>) child.accept( this );
|
||||
}
|
||||
else {
|
||||
trimCharText = unquoteStringLiteral( ctx.getText() );
|
||||
if ( trimCharText.length() != 1 ) {
|
||||
throw new SemanticException( "Trim character for trim() function must be single character, found '" + trimCharText + "'" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new SqmLiteral<>(
|
||||
trimCharText.charAt( 0 ),
|
||||
resolveExpressibleTypeBasic( Character.class ),
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.hibernate.type.descriptor.jdbc.CharJdbcType;
|
|||
import org.hibernate.type.internal.BasicTypeImpl;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -41,65 +42,62 @@ import org.mockito.Mockito;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
* Tests correct rendering of trim function emulation for {@link org.hibernate.dialect.AbstractTransactSQLDialect} dialects.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@ServiceRegistry
|
||||
public class AnsiTrimEmulationFunctionTest {
|
||||
private static final String trimSource = "a.column";
|
||||
private static final String LEADING = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)+1)";
|
||||
private static final String TRAILING = "substring(?1,1,len(?1)-patindex('%[^'+?2+']%',reverse(?1))+1)";
|
||||
private static final String BOTH = "substring(?1,patindex('%[^'+?2+']%',?1),len(?1)-patindex('%[^'+?2+']%',?1)-patindex('%[^'+?2+']%',reverse(?1))+2)";
|
||||
|
||||
@Test
|
||||
@RequiresDialect( SQLServerDialect.class )
|
||||
public void testBasicSqlServerProcessing(ServiceRegistryScope scope) {
|
||||
Dialect dialect = new SQLServerDialect();
|
||||
TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() );
|
||||
|
||||
performBasicSpaceTrimmingTests( dialect, scope.getRegistry(), function );
|
||||
|
||||
final String expectedTrimPrep = "replace(replace(a.column,' ','#%#%'),'-',' ')";
|
||||
final String expectedPostTrimPrefix = "replace(replace(";
|
||||
final String expectedPostTrimSuffix = ",' ','-'),'#%#%',' ')";
|
||||
|
||||
// -> trim(LEADING '-' FROM a.column)
|
||||
String rendered = render( dialect, scope.getRegistry(), function, TrimSpec.LEADING, '-', trimSource );
|
||||
String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||
String expected = LEADING.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
|
||||
// -> trim(TRAILING '-' FROM a.column)
|
||||
rendered = render( dialect, scope.getRegistry(), function, TrimSpec.TRAILING, '-', trimSource );
|
||||
expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||
expected = TRAILING.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
|
||||
// -> trim(BOTH '-' FROM a.column)
|
||||
rendered = render( dialect, scope.getRegistry(), function, TrimSpec.BOTH, '-', trimSource );
|
||||
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||
expected = BOTH.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresDialect( SybaseDialect.class )
|
||||
public void testBasicSybaseProcessing(ServiceRegistryScope scope) {
|
||||
Dialect dialect = new SybaseDialect();
|
||||
TrimFunction function = new TrimFunction( dialect, new TypeConfiguration() );
|
||||
|
||||
performBasicSpaceTrimmingTests( dialect, scope.getRegistry(), function );
|
||||
|
||||
final String expectedTrimPrep = "str_replace(str_replace(a.column,' ','#%#%'),'-',' ')";
|
||||
final String expectedPostTrimPrefix = "str_replace(str_replace(";
|
||||
final String expectedPostTrimSuffix = ",' ','-'),'#%#%',' ')";
|
||||
|
||||
// -> trim(LEADING '-' FROM a.column)
|
||||
String rendered = render( dialect, scope.getRegistry(), function, TrimSpec.LEADING, '-', trimSource );
|
||||
String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||
String expected = LEADING.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
|
||||
// -> trim(TRAILING '-' FROM a.column)
|
||||
rendered = render( dialect, scope.getRegistry(), function, TrimSpec.TRAILING, '-', trimSource );
|
||||
expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||
expected = TRAILING.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
|
||||
// -> trim(BOTH '-' FROM a.column)
|
||||
rendered = render( dialect, scope.getRegistry(), function, TrimSpec.BOTH, '-', trimSource );
|
||||
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||
expected = BOTH.replace( "?1", trimSource ).replace( "?2", "'-'" );
|
||||
assertEquals( expected, rendered );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue