HHH-3701 : trim function emulation for sybase
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15860 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
5e77669afd
commit
1e45c666e6
|
@ -0,0 +1,227 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Middleware LLC.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.QueryException;
|
||||||
|
import org.hibernate.engine.Mapping;
|
||||||
|
import org.hibernate.engine.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link org.hibernate.dialect.function.SQLFunction} providing support for implementing TRIM functionality
|
||||||
|
* (as defined by both the ANSI SQL and JPA specs) in cases where the dialect may not support the full <tt>trim</tt>
|
||||||
|
* function itself.
|
||||||
|
* <p/>
|
||||||
|
* Follows the <a href="http://en.wikipedia.org/wiki/Template_method_pattern">template</a> pattern in order to implement
|
||||||
|
* the {@link #render} method.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction {
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public final Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
|
||||||
|
return Hibernate.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public final boolean hasArguments() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public final boolean hasParenthesesIfNoArguments() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public final String render(List args, SessionFactoryImplementor factory) throws QueryException {
|
||||||
|
// According to both the ANSI-SQL and JPA specs, trim takes a variable number of parameters between 1 and 4.
|
||||||
|
// at least one paramer (trimSource) is required. From the SQL spec:
|
||||||
|
//
|
||||||
|
// <trim function> ::=
|
||||||
|
// TRIM <left paren> <trim operands> <right paren>
|
||||||
|
//
|
||||||
|
// <trim operands> ::=
|
||||||
|
// [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
|
||||||
|
//
|
||||||
|
// <trim specification> ::=
|
||||||
|
// LEADING
|
||||||
|
// | TRAILING
|
||||||
|
// | BOTH
|
||||||
|
//
|
||||||
|
// If <trim specification> is omitted, BOTH is assumed.
|
||||||
|
// If <trim character> is omitted, space is assumed
|
||||||
|
if ( args.size() == 1 ) {
|
||||||
|
// we have the form: trim(trimSource)
|
||||||
|
// so we trim leading and trailing spaces
|
||||||
|
return resolveBothSpaceTrimFunction().render( args, factory ); // EARLY EXIT!!!!
|
||||||
|
}
|
||||||
|
else if ( "from".equalsIgnoreCase( ( String ) args.get( 0 ) ) ) {
|
||||||
|
// we have the form: trim(from trimSource).
|
||||||
|
// This is functionally equivalent to trim(trimSource)
|
||||||
|
return resolveBothSpaceTrimFromFunction().render( args, factory ); // EARLY EXIT!!!!
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// otherwise, a trim-specification and/or a trim-character
|
||||||
|
// have been specified; we need to decide which options
|
||||||
|
// are present and "do the right thing"
|
||||||
|
boolean leading = true; // should leading trim-characters be trimmed?
|
||||||
|
boolean trailing = true; // should trailing trim-characters be trimmed?
|
||||||
|
String trimCharacter; // the trim-character (what is to be trimmed off?)
|
||||||
|
String trimSource; // the trim-source (from where should it be trimmed?)
|
||||||
|
|
||||||
|
// potentialTrimCharacterArgIndex = 1 assumes that a
|
||||||
|
// trim-specification has been specified. we handle the
|
||||||
|
// exception to that explicitly
|
||||||
|
int potentialTrimCharacterArgIndex = 1;
|
||||||
|
String firstArg = ( String ) args.get( 0 );
|
||||||
|
if ( "leading".equalsIgnoreCase( firstArg ) ) {
|
||||||
|
trailing = false;
|
||||||
|
}
|
||||||
|
else if ( "trailing".equalsIgnoreCase( firstArg ) ) {
|
||||||
|
leading = false;
|
||||||
|
}
|
||||||
|
else if ( "both".equalsIgnoreCase( firstArg ) ) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
potentialTrimCharacterArgIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
String potentialTrimCharacter = ( String ) args.get( potentialTrimCharacterArgIndex );
|
||||||
|
if ( "from".equalsIgnoreCase( potentialTrimCharacter ) ) {
|
||||||
|
trimCharacter = "' '";
|
||||||
|
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
|
||||||
|
}
|
||||||
|
else if ( potentialTrimCharacterArgIndex + 1 >= args.size() ) {
|
||||||
|
trimCharacter = "' '";
|
||||||
|
trimSource = potentialTrimCharacter;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
trimCharacter = potentialTrimCharacter;
|
||||||
|
if ( "from".equalsIgnoreCase( ( String ) args.get( potentialTrimCharacterArgIndex + 1 ) ) ) {
|
||||||
|
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 2 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List argsToUse = new ArrayList();
|
||||||
|
argsToUse.add( trimSource );
|
||||||
|
argsToUse.add( trimCharacter );
|
||||||
|
|
||||||
|
if ( trimCharacter.equals( "' '" ) ) {
|
||||||
|
if ( leading && trailing ) {
|
||||||
|
return resolveBothSpaceTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
else if ( leading ) {
|
||||||
|
return resolveLeadingSpaceTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return resolveTrailingSpaceTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( leading && trailing ) {
|
||||||
|
return resolveBothTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
else if ( leading ) {
|
||||||
|
return resolveLeadingTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return resolveTrailingTrimFunction().render( argsToUse, factory );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim both leading and trailing spaces.
|
||||||
|
* <p/>
|
||||||
|
* In this form, the imput arguments is missing the <tt>FROM</tt> keyword.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveBothSpaceTrimFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim both leading and trailing spaces.
|
||||||
|
* <p/>
|
||||||
|
* The same as {#link resolveBothSpaceTrimFunction} except that here the<tt>FROM</tt> is included and
|
||||||
|
* will need to be accounted for during {@link SQLFunction#render} processing.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveBothSpaceTrimFromFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim leading spaces.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveLeadingSpaceTrimFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim trailing spaces.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveTrailingSpaceTrimFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim the specified character from both the
|
||||||
|
* beginning (leading) and end (trailing) of the trim source.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveBothTrimFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim the specified character from the
|
||||||
|
* beginning (leading) of the trim source.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveLeadingTrimFunction();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the function definition which should be used to trim the specified character from the
|
||||||
|
* end (trailing) of the trim source.
|
||||||
|
*
|
||||||
|
* @return The sql function
|
||||||
|
*/
|
||||||
|
protected abstract SQLFunction resolveTrailingTrimFunction();
|
||||||
|
}
|
|
@ -25,13 +25,6 @@
|
||||||
package org.hibernate.dialect.function;
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.QueryException;
|
|
||||||
import org.hibernate.engine.Mapping;
|
|
||||||
import org.hibernate.engine.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.type.Type;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link SQLFunction} implementation that emulates the ANSI SQL trim function
|
* A {@link SQLFunction} implementation that emulates the ANSI SQL trim function
|
||||||
|
@ -42,129 +35,222 @@ import java.util.ArrayList;
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class AnsiTrimEmulationFunction implements SQLFunction {
|
public class AnsiTrimEmulationFunction extends AbstractAnsiTrimEmulationFunction {
|
||||||
|
public static final String LTRIM = "ltrim";
|
||||||
|
public static final String RTRIM = "rtrim";
|
||||||
|
public static final String REPLACE = "replace";
|
||||||
|
public static final String SPACE_PLACEHOLDER = "${space}$";
|
||||||
|
|
||||||
private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( ?1 )");
|
public static final String LEADING_SPACE_TRIM_TEMPLATE = LTRIM + "(?1)";
|
||||||
private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "rtrim( ?1 )");
|
public static final String TRAILING_SPACE_TRIM_TEMPLATE = RTRIM + "(?1)";
|
||||||
private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?1 ) )");
|
public static final String BOTH_SPACE_TRIM_TEMPLATE = LTRIM + "(" + RTRIM + "(?1))";
|
||||||
private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?2 ) )");
|
public static final String BOTH_SPACE_TRIM_FROM_TEMPLATE = LTRIM + "(" + RTRIM + "(?2))"; //skip the FROM keyword in params
|
||||||
|
|
||||||
private static final SQLFunction LEADING_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( rtrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ), ' ', ?2 ), '${space}$', ' ' )" );
|
/**
|
||||||
private static final SQLFunction TRAILING_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( ltrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ), ' ', ?2 ), '${space}$', ' ' )" );
|
* A template for the series of calls required to trim non-space chars from the beginning of text.
|
||||||
private static final SQLFunction BOTH_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( ltrim( rtrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ) ), ' ', ?2 ), '${space}$', ' ' )" );
|
* <p/>
|
||||||
|
* NOTE : essentially we:</ol>
|
||||||
|
* <li>replace all space chars with the text '${space}$'</li>
|
||||||
|
* <li>replace all the actual replacement chars with space chars</li>
|
||||||
|
* <li>perform left-trimming (that removes any of the space chars we just added which occur at the beginning of the text)</li>
|
||||||
|
* <li>replace all space chars with the replacement char</li>
|
||||||
|
* <li>replace all the '${space}$' text with space chars</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
public static final String LEADING_TRIM_TEMPLATE =
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
LTRIM + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
"?1," +
|
||||||
|
"' '," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'" +
|
||||||
|
")," +
|
||||||
|
"?2," +
|
||||||
|
"' '" +
|
||||||
|
")" +
|
||||||
|
")," +
|
||||||
|
"' '," +
|
||||||
|
"?2" +
|
||||||
|
")," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'," +
|
||||||
|
"' '" +
|
||||||
|
")";
|
||||||
|
|
||||||
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
|
/**
|
||||||
return Hibernate.STRING;
|
* A template for the series of calls required to trim non-space chars from the end of text.
|
||||||
|
* <p/>
|
||||||
|
* NOTE: essentially the same series of calls as outlined in {@link #LEADING_TRIM_TEMPLATE} except that here,
|
||||||
|
* instead of left-trimming the added spaces, we right-trim them to remove them from the end of the text.
|
||||||
|
*/
|
||||||
|
public static final String TRAILING_TRIM_TEMPLATE =
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
RTRIM + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
"?1," +
|
||||||
|
"' '," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'" +
|
||||||
|
")," +
|
||||||
|
"?2," +
|
||||||
|
"' '" +
|
||||||
|
")" +
|
||||||
|
")," +
|
||||||
|
"' '," +
|
||||||
|
"?2" +
|
||||||
|
")," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'," +
|
||||||
|
"' '" +
|
||||||
|
")";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A template for the series of calls required to trim non-space chars from both the beginning and the end of text.
|
||||||
|
* <p/>
|
||||||
|
* NOTE: again, we have a series of calls that is essentially the same as outlined in {@link #LEADING_TRIM_TEMPLATE}
|
||||||
|
* except that here we perform both left (leading) and right (trailing) trimming.
|
||||||
|
*/
|
||||||
|
public static final String BOTH_TRIM_TEMPLATE =
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
LTRIM + "(" +
|
||||||
|
RTRIM + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
REPLACE + "(" +
|
||||||
|
"?1," +
|
||||||
|
"' '," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'" +
|
||||||
|
")," +
|
||||||
|
"?2," +
|
||||||
|
"' '" +
|
||||||
|
")" +
|
||||||
|
")" +
|
||||||
|
")," +
|
||||||
|
"' '," +
|
||||||
|
"?2" +
|
||||||
|
")," +
|
||||||
|
"'" + SPACE_PLACEHOLDER + "'," +
|
||||||
|
"' '" +
|
||||||
|
")";
|
||||||
|
|
||||||
|
private final SQLFunction leadingSpaceTrim;
|
||||||
|
private final SQLFunction trailingSpaceTrim;
|
||||||
|
private final SQLFunction bothSpaceTrim;
|
||||||
|
private final SQLFunction bothSpaceTrimFrom;
|
||||||
|
|
||||||
|
private final SQLFunction leadingTrim;
|
||||||
|
private final SQLFunction trailingTrim;
|
||||||
|
private final SQLFunction bothTrim;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new AnsiTrimEmulationFunction using {@link #LTRIM}, {@link #RTRIM}, and {@link #REPLACE}
|
||||||
|
* respectively.
|
||||||
|
*
|
||||||
|
* @see #AnsiTrimEmulationFunction(String,String,String)
|
||||||
|
*/
|
||||||
|
public AnsiTrimEmulationFunction() {
|
||||||
|
this( LTRIM, RTRIM, REPLACE );
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasArguments() {
|
/**
|
||||||
return true;
|
* Constructs a <tt>trim()</tt> emulation function definition using the specified function calls.
|
||||||
|
*
|
||||||
|
* @param ltrimFunctionName The <tt>left trim</tt> function to use.
|
||||||
|
* @param rtrimFunctionName The <tt>right trim</tt> function to use.
|
||||||
|
* @param replaceFunctionName The <tt>replace</tt> function to use.
|
||||||
|
*/
|
||||||
|
public AnsiTrimEmulationFunction(String ltrimFunctionName, String rtrimFunctionName, String replaceFunctionName) {
|
||||||
|
leadingSpaceTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
LEADING_SPACE_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
trailingSpaceTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
TRAILING_SPACE_TRIM_TEMPLATE.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
bothSpaceTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
BOTH_SPACE_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
bothSpaceTrimFrom = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
BOTH_SPACE_TRIM_FROM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
leadingTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
LEADING_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
.replaceAll( REPLACE,replaceFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
trailingTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
TRAILING_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
.replaceAll( REPLACE,replaceFunctionName )
|
||||||
|
);
|
||||||
|
|
||||||
|
bothTrim = new SQLFunctionTemplate(
|
||||||
|
Hibernate.STRING,
|
||||||
|
BOTH_TRIM_TEMPLATE.replaceAll( LTRIM, ltrimFunctionName )
|
||||||
|
.replaceAll( RTRIM, rtrimFunctionName )
|
||||||
|
.replaceAll( REPLACE,replaceFunctionName )
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasParenthesesIfNoArguments() {
|
/**
|
||||||
return false;
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
protected SQLFunction resolveBothSpaceTrimFunction() {
|
||||||
|
return bothSpaceTrim;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
|
/**
|
||||||
// according to both the ANSI-SQL and EJB3 specs, trim can either take
|
* {@inheritDoc}
|
||||||
// exactly one parameter or a variable number of parameters between 1 and 4.
|
*/
|
||||||
// from the SQL spec:
|
protected SQLFunction resolveBothSpaceTrimFromFunction() {
|
||||||
//
|
return bothSpaceTrimFrom;
|
||||||
// <trim function> ::=
|
}
|
||||||
// TRIM <left paren> <trim operands> <right paren>
|
|
||||||
//
|
|
||||||
// <trim operands> ::=
|
|
||||||
// [ [ <trim specification> ] [ <trim character> ] FROM ] <trim source>
|
|
||||||
//
|
|
||||||
// <trim specification> ::=
|
|
||||||
// LEADING
|
|
||||||
// | TRAILING
|
|
||||||
// | BOTH
|
|
||||||
//
|
|
||||||
// If only <trim specification> is omitted, BOTH is assumed;
|
|
||||||
// if <trim character> is omitted, space is assumed
|
|
||||||
if ( args.size() == 1 ) {
|
|
||||||
// we have the form: trim(trimSource)
|
|
||||||
// so we trim leading and trailing spaces
|
|
||||||
return BOTH_SPACE_TRIM.render( args, factory );
|
|
||||||
}
|
|
||||||
else if ( "from".equalsIgnoreCase( ( String ) args.get( 0 ) ) ) {
|
|
||||||
// we have the form: trim(from trimSource).
|
|
||||||
// This is functionally equivalent to trim(trimSource)
|
|
||||||
return BOTH_SPACE_TRIM_FROM.render( args, factory );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// otherwise, a trim-specification and/or a trim-character
|
|
||||||
// have been specified; we need to decide which options
|
|
||||||
// are present and "do the right thing"
|
|
||||||
boolean leading = true; // should leading trim-characters be trimmed?
|
|
||||||
boolean trailing = true; // should trailing trim-characters be trimmed?
|
|
||||||
String trimCharacter = null; // the trim-character
|
|
||||||
String trimSource = null; // the trim-source
|
|
||||||
|
|
||||||
// potentialTrimCharacterArgIndex = 1 assumes that a
|
/**
|
||||||
// trim-specification has been specified. we handle the
|
* {@inheritDoc}
|
||||||
// exception to that explicitly
|
*/
|
||||||
int potentialTrimCharacterArgIndex = 1;
|
protected SQLFunction resolveLeadingSpaceTrimFunction() {
|
||||||
String firstArg = ( String ) args.get( 0 );
|
return leadingSpaceTrim;
|
||||||
if ( "leading".equalsIgnoreCase( firstArg ) ) {
|
}
|
||||||
trailing = false;
|
|
||||||
}
|
|
||||||
else if ( "trailing".equalsIgnoreCase( firstArg ) ) {
|
|
||||||
leading = false;
|
|
||||||
}
|
|
||||||
else if ( "both".equalsIgnoreCase( firstArg ) ) {
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
potentialTrimCharacterArgIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String potentialTrimCharacter = ( String ) args.get( potentialTrimCharacterArgIndex );
|
/**
|
||||||
if ( "from".equalsIgnoreCase( potentialTrimCharacter ) ) {
|
* {@inheritDoc}
|
||||||
trimCharacter = "' '";
|
*/
|
||||||
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
|
protected SQLFunction resolveTrailingSpaceTrimFunction() {
|
||||||
}
|
return trailingSpaceTrim;
|
||||||
else if ( potentialTrimCharacterArgIndex + 1 >= args.size() ) {
|
}
|
||||||
trimCharacter = "' '";
|
|
||||||
trimSource = potentialTrimCharacter;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trimCharacter = potentialTrimCharacter;
|
|
||||||
if ( "from".equalsIgnoreCase( ( String ) args.get( potentialTrimCharacterArgIndex + 1 ) ) ) {
|
|
||||||
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 2 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trimSource = ( String ) args.get( potentialTrimCharacterArgIndex + 1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List argsToUse = null;
|
/**
|
||||||
argsToUse = new ArrayList();
|
* {@inheritDoc}
|
||||||
argsToUse.add( trimSource );
|
*/
|
||||||
argsToUse.add( trimCharacter );
|
protected SQLFunction resolveBothTrimFunction() {
|
||||||
|
return bothTrim;
|
||||||
|
}
|
||||||
|
|
||||||
if ( trimCharacter.equals( "' '" ) ) {
|
/**
|
||||||
if ( leading && trailing ) {
|
* {@inheritDoc}
|
||||||
return BOTH_SPACE_TRIM.render( argsToUse, factory );
|
*/
|
||||||
}
|
protected SQLFunction resolveLeadingTrimFunction() {
|
||||||
else if ( leading ) {
|
return leadingTrim;
|
||||||
return LEADING_SPACE_TRIM.render( argsToUse, factory );
|
}
|
||||||
}
|
|
||||||
else {
|
/**
|
||||||
return TRAILING_SPACE_TRIM.render( argsToUse, factory );
|
* {@inheritDoc}
|
||||||
}
|
*/
|
||||||
}
|
protected SQLFunction resolveTrailingTrimFunction() {
|
||||||
else {
|
return trailingTrim;
|
||||||
if ( leading && trailing ) {
|
|
||||||
return BOTH_TRIM.render( argsToUse, factory );
|
|
||||||
}
|
|
||||||
else if ( leading ) {
|
|
||||||
return LEADING_TRIM.render( argsToUse, factory );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return TRAILING_TRIM.render( argsToUse, factory );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
|
||||||
|
* indicated by the @author tags or express copyright attribution
|
||||||
|
* statements applied by the authors. All third-party contributions are
|
||||||
|
* distributed under license by Red Hat Middleware LLC.
|
||||||
|
*
|
||||||
|
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||||
|
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||||
|
* Lesser General Public License, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this distribution; if not, write to:
|
||||||
|
* Free Software Foundation, Inc.
|
||||||
|
* 51 Franklin Street, Fifth Floor
|
||||||
|
* Boston, MA 02110-1301 USA
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.dialect.function;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.function.AnsiTrimEmulationFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO : javadoc
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class AnsiTrimEmulationFunctionTest extends TestCase {
|
||||||
|
private static final String trimSource = "a.column";
|
||||||
|
|
||||||
|
public void testBasicSqlServerProcessing() {
|
||||||
|
AnsiTrimEmulationFunction function = new AnsiTrimEmulationFunction();
|
||||||
|
|
||||||
|
performBasicSpaceTrimmingTests( function );
|
||||||
|
|
||||||
|
final String expectedTrimPrep = "replace(replace(a.column,' ','${space}$'),'-',' ')";
|
||||||
|
final String expectedPostTrimPrefix = "replace(replace(";
|
||||||
|
final String expectedPostTrimSuffix = ",' ','-'),'${space}$',' ')";
|
||||||
|
|
||||||
|
// -> trim(LEADING '-' FROM a.column)
|
||||||
|
String rendered = function.render( argList( "LEADING", "'-'", "FROM", trimSource ), null );
|
||||||
|
String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim(TRAILING '-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "TRAILING", "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim(BOTH '-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "BOTH", "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim('-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasicSybaseProcessing() {
|
||||||
|
AnsiTrimEmulationFunction function = new AnsiTrimEmulationFunction(
|
||||||
|
AnsiTrimEmulationFunction.LTRIM,
|
||||||
|
AnsiTrimEmulationFunction.RTRIM,
|
||||||
|
"str_replace"
|
||||||
|
);
|
||||||
|
|
||||||
|
performBasicSpaceTrimmingTests( function );
|
||||||
|
|
||||||
|
final String expectedTrimPrep = "str_replace(str_replace(a.column,' ','${space}$'),'-',' ')";
|
||||||
|
final String expectedPostTrimPrefix = "str_replace(str_replace(";
|
||||||
|
final String expectedPostTrimSuffix = ",' ','-'),'${space}$',' ')";
|
||||||
|
|
||||||
|
// -> trim(LEADING '-' FROM a.column)
|
||||||
|
String rendered = function.render( argList( "LEADING", "'-'", "FROM", trimSource ), null );
|
||||||
|
String expected = expectedPostTrimPrefix + "ltrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim(TRAILING '-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "TRAILING", "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "rtrim(" + expectedTrimPrep + ")" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim(BOTH '-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "BOTH", "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
|
||||||
|
// -> trim('-' FROM a.column)
|
||||||
|
rendered = function.render( argList( "'-'", "FROM", trimSource ), null );
|
||||||
|
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
|
||||||
|
assertEquals( expected, rendered );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void performBasicSpaceTrimmingTests(AnsiTrimEmulationFunction function) {
|
||||||
|
// -> trim(a.column)
|
||||||
|
String rendered = function.render( argList( trimSource ), null );
|
||||||
|
assertEquals( "ltrim(rtrim(a.column))", rendered );
|
||||||
|
|
||||||
|
// -> trim(FROM a.column)
|
||||||
|
rendered = function.render( argList( "FROM", trimSource ), null );
|
||||||
|
assertEquals( "ltrim(rtrim(a.column))", rendered );
|
||||||
|
|
||||||
|
// -> trim(BOTH FROM a.column)
|
||||||
|
rendered = function.render( argList( "BOTH", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "ltrim(rtrim(a.column))", rendered );
|
||||||
|
|
||||||
|
// -> trim(BOTH ' ' FROM a.column)
|
||||||
|
rendered = function.render( argList( "BOTH", "' '", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "ltrim(rtrim(a.column))", rendered );
|
||||||
|
|
||||||
|
// -> trim(LEADING FROM a.column)
|
||||||
|
rendered = function.render( argList( "LEADING", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "ltrim(a.column)", rendered );
|
||||||
|
|
||||||
|
// -> trim(LEADING ' ' FROM a.column)
|
||||||
|
rendered = function.render( argList( "LEADING", "' '", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "ltrim(a.column)", rendered );
|
||||||
|
|
||||||
|
// -> trim(TRAILING FROM a.column)
|
||||||
|
rendered = function.render( argList( "TRAILING", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "rtrim(a.column)", rendered );
|
||||||
|
|
||||||
|
// -> trim(TRAILING ' ' FROM a.column)
|
||||||
|
rendered = function.render( argList( "TRAILING", "' '", "FROM", trimSource ), null );
|
||||||
|
assertEquals( "rtrim(a.column)", rendered );
|
||||||
|
}
|
||||||
|
|
||||||
|
private List argList(String arg) {
|
||||||
|
ArrayList args = new ArrayList();
|
||||||
|
args.add( arg );
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List argList(String arg1, String arg2) {
|
||||||
|
ArrayList args = new ArrayList();
|
||||||
|
args.add( arg1 );
|
||||||
|
args.add( arg2 );
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List argList(String arg1, String arg2, String arg3) {
|
||||||
|
ArrayList args = new ArrayList();
|
||||||
|
args.add( arg1 );
|
||||||
|
args.add( arg2 );
|
||||||
|
args.add( arg3 );
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List argList(String arg1, String arg2, String arg3, String arg4) {
|
||||||
|
ArrayList args = new ArrayList();
|
||||||
|
args.add( arg1 );
|
||||||
|
args.add( arg2 );
|
||||||
|
args.add( arg3 );
|
||||||
|
args.add( arg4 );
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue