From: <hib...@li...> - 2006-08-04 18:38:37
|
Author: ste...@jb... Date: 2006-08-04 14:37:47 -0400 (Fri, 04 Aug 2006) New Revision: 10221 Added: trunk/Hibernate3/src/org/hibernate/dialect/ResultColumnReferenceStrategy.java Removed: trunk/Hibernate3/src/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java Modified: trunk/Hibernate3/src/org/hibernate/dialect/DerbyDialect.java trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java Log: added ResultColumnReferenceStrategy (not yet being utilized); Derby trim() function (w/o capability to specify replacement character) Modified: trunk/Hibernate3/src/org/hibernate/dialect/DerbyDialect.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/DerbyDialect.java 2006-08-04 18:25:19 UTC (rev 10220) +++ trunk/Hibernate3/src/org/hibernate/dialect/DerbyDialect.java 2006-08-04 18:37:47 UTC (rev 10221) @@ -2,12 +2,22 @@ package org.hibernate.dialect; import org.hibernate.Hibernate; +import org.hibernate.QueryException; +import org.hibernate.HibernateException; +import org.hibernate.engine.Mapping; +import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.type.Type; import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.function.AnsiTrimEmulationFunction; +import org.hibernate.dialect.function.SQLFunction; +import org.hibernate.dialect.function.SQLFunctionTemplate; import org.hibernate.id.TableHiLoGenerator; import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DerbyCaseFragment; +import java.util.List; +import java.util.ArrayList; + /** * @author Simon Johnston * @@ -21,7 +31,7 @@ public DerbyDialect() { super(); registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) ); - registerFunction( "trim", new AnsiTrimEmulationFunction() ); + registerFunction( "trim", new DerbyTrimFunctionEmulation() ); } /** @@ -38,18 +48,18 @@ return new DerbyCaseFragment(); } - public boolean dropConstraints() { - return true; + public boolean dropConstraints() { + return true; } - + public Class getNativeIdentifierGeneratorClass() { return TableHiLoGenerator.class; } - + public boolean supportsSequences() { return false; } - + public boolean supportsLimit() { return false; } @@ -60,5 +70,124 @@ public String getQuerySequencesString() { return null ; - } + } + + /** + * A specialized function template to emulate the ANSI trim function on Derby DB + * since it does not support the full trim specification. However, we cannot even + * fully emulate it because there is not standard 'replace' function either. :( + */ + public static class DerbyTrimFunctionEmulation implements SQLFunction { + private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( ?1 )"); + private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "rtrim( ?1 )"); + private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?1 ) )"); + private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?2 ) )"); + + public Type getReturnType(Type columnType, Mapping mapping) throws QueryException { + return Hibernate.STRING; + } + + public boolean hasArguments() { + return true; + } + + public boolean hasParenthesesIfNoArguments() { + return false; + } + + public String render(List args, SessionFactoryImplementor factory) throws QueryException { + // according to both the ANSI-SQL and EJB3 specs, trim can either take + // exactly one parameter or a variable number of parameters between 1 and 4. + // 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 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 + // 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 = null; + argsToUse = new ArrayList(); + argsToUse.add( trimSource ); + argsToUse.add( trimCharacter ); + + if ( trimCharacter.equals( "' '" ) ) { + if ( leading && trailing ) { + return BOTH_SPACE_TRIM.render( argsToUse, factory ); + } + else if ( leading ) { + return LEADING_SPACE_TRIM.render( argsToUse, factory ); + } + else { + return TRAILING_SPACE_TRIM.render( argsToUse, factory ); + } + } + else { + throw new HibernateException( "cannot specify trim character when using Derby as Derby does not support the ANSI trim function, not does it support a replace function to properly emmulate it" ); + } + } + } + } } Modified: trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-08-04 18:25:19 UTC (rev 10220) +++ trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-08-04 18:37:47 UTC (rev 10221) @@ -1079,4 +1079,49 @@ // return false here, as most databases do not properly support this construct... return false; } + + /** + * Which strategy should be used to reference columns in the "result" when + * performing sorting (i.e. when processing an order by). This is + * defined by ANSI SQL as the manner in which columns can be referred to in the + * group-by, having, and order-by clauses. + * <p/> + * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that + * is the most performant. + * + * @return The strategy to use. + */ + public ResultColumnReferenceStrategy getOrderByColumnReferenceStrategy() { + return ResultColumnReferenceStrategy.SOURCE; + } + + /** + * Which strategy should be used to reference columns in the "result" when + * performing aggregation (i.e. when processing a group by). This is + * defined by ANSI SQL as the manner in which columns can be referred to in the + * group-by, having, and order-by clauses. + * <p/> + * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that + * is the most performant. + * + * @return The strategy to use. + */ + public ResultColumnReferenceStrategy getGroupByColumnReferenceStrategy() { + return ResultColumnReferenceStrategy.SOURCE; + } + + /** + * Which strategy should be used to reference columns in the "result" when + * performing aggregated restirctiuon(i.e. when processing a having). This is + * defined by ANSI SQL as the manner in which columns can be referred to in the + * group-by, having, and order-by clauses. + * <p/> + * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that + * is the most performant. + * + * @return The strategy to use. + */ + public ResultColumnReferenceStrategy getHavingColumnReferenceStrategy() { + return ResultColumnReferenceStrategy.SOURCE; + } } Added: trunk/Hibernate3/src/org/hibernate/dialect/ResultColumnReferenceStrategy.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/ResultColumnReferenceStrategy.java 2006-08-04 18:25:19 UTC (rev 10220) +++ trunk/Hibernate3/src/org/hibernate/dialect/ResultColumnReferenceStrategy.java 2006-08-04 18:37:47 UTC (rev 10221) @@ -0,0 +1,67 @@ +package org.hibernate.dialect; + +import java.io.Serializable; +import java.io.ObjectStreamException; +import java.util.Map; +import java.util.HashMap; + +/** + * Defines how we need to reference columns in the group-by, having, and order-by + * clauses. + * + * @author Steve Ebersole + */ +public class ResultColumnReferenceStrategy implements Serializable { + + private static final Map INSTANCES = new HashMap(); + + /** + * This strategy says to reference the result columns by the qualified column name + * found in the result source. This strategy is not strictly allowed by ANSI SQL + * but is Hibernate's legacy behavior and is also the fastest of the strategies; thus + * it should be used if supported by the underlying database. + */ + public static final ResultColumnReferenceStrategy SOURCE = new ResultColumnReferenceStrategy( "source"); + + /** + * For databases which do not support {@link #SOURCE}, ANSI SQL defines two allowable + * approaches. One is to reference the result column by the alias it is given in the + * result source (if it is given an alias). This strategy says to use this approach. + * <p/> + * The other QNSI SQL compliant approach is {@link #ORDINAL}. + */ + public static final ResultColumnReferenceStrategy ALIAS = new ResultColumnReferenceStrategy( "alias" ); + + /** + * For databases which do not support {@link #SOURCE}, ANSI SQL defines two allowable + * approaches. One is to reference the result column by the ordinal position at which + * it appears in the result source. This strategy says to use this approach. + * <p/> + * The other QNSI SQL compliant approach is {@link #ALIAS}. + */ + public static final ResultColumnReferenceStrategy ORDINAL = new ResultColumnReferenceStrategy( "ordinal" ); + + static { + ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.SOURCE.name, ResultColumnReferenceStrategy.SOURCE ); + ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.ALIAS.name, ResultColumnReferenceStrategy.ALIAS ); + ResultColumnReferenceStrategy.INSTANCES.put( ResultColumnReferenceStrategy.ORDINAL.name, ResultColumnReferenceStrategy.ORDINAL ); + } + + private final String name; + + public ResultColumnReferenceStrategy(String name) { + this.name = name; + } + + public String toString() { + return name; + } + + private Object readResolve() throws ObjectStreamException { + return parse( name ); + } + + public static ResultColumnReferenceStrategy parse(String name) { + return ( ResultColumnReferenceStrategy ) ResultColumnReferenceStrategy.INSTANCES.get( name ); + } +} Deleted: trunk/Hibernate3/src/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java 2006-08-04 18:25:19 UTC (rev 10220) +++ trunk/Hibernate3/src/org/hibernate/dialect/function/AnsiTrimEmulationFunction.java 2006-08-04 18:37:47 UTC (rev 10221) @@ -1,146 +0,0 @@ -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 SQLFunction} implementation that emulates the ANSI SQL trim function - * on dialects which do not support the full definition. However, this function - * definition does assume the availability of ltrim, rtrim, and replace functions - * which it uses in various combinations to emulate the desired ANSI trim() - * functionality. - * - * @author Steve Ebersole - */ -public class AnsiTrimEmulationFunction implements SQLFunction { - - private static final SQLFunction LEADING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( ?1 )"); - private static final SQLFunction TRAILING_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "rtrim( ?1 )"); - private static final SQLFunction BOTH_SPACE_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?1 ) )"); - private static final SQLFunction BOTH_SPACE_TRIM_FROM = new SQLFunctionTemplate( Hibernate.STRING, "ltrim( rtrim( ?2 ) )"); - - 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}$', ' ' )" ); - private static final SQLFunction BOTH_TRIM = new SQLFunctionTemplate( Hibernate.STRING, "replace( replace( ltrim( rtrim( replace( replace( ?1, ' ', '${space}$' ), ?2, ' ' ) ) ), ' ', ?2 ), '${space}$', ' ' )" ); - - public Type getReturnType(Type columnType, Mapping mapping) throws QueryException { - return Hibernate.STRING; - } - - public boolean hasArguments() { - return true; - } - - public boolean hasParenthesesIfNoArguments() { - return false; - } - - public String render(List args, SessionFactoryImplementor factory) throws QueryException { - // according to both the ANSI-SQL and EJB3 specs, trim can either take - // exactly one parameter or a variable number of parameters between 1 and 4. - // 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 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 - // 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 = null; - argsToUse = new ArrayList(); - argsToUse.add( trimSource ); - argsToUse.add( trimCharacter ); - - if ( trimCharacter.equals( "' '" ) ) { - if ( leading && trailing ) { - return BOTH_SPACE_TRIM.render( argsToUse, factory ); - } - else if ( leading ) { - return LEADING_SPACE_TRIM.render( argsToUse, factory ); - } - else { - return TRAILING_SPACE_TRIM.render( argsToUse, factory ); - } - } - else { - 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 ); - } - } - } - } -} |