HHH-5212 - Alter SQLFunction contract to be more flexible

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@20097 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-08-02 18:51:45 +00:00
parent fbe7d325e2
commit 217898d8aa
33 changed files with 443 additions and 408 deletions

View File

@ -72,9 +72,9 @@ public class AggregateProjection extends SimpleProjection {
/**
* {@inheritDoc}
*/
public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery)
throws HibernateException {
public String toSqlString(Criteria criteria, int loc, CriteriaQuery criteriaQuery) throws HibernateException {
final String functionFragment = getFunction( criteriaQuery ).render(
criteriaQuery.getType( criteria, getPropertyName() ),
buildFunctionParameterList( criteria, criteriaQuery ),
criteriaQuery.getFactory()
);

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.criterion;
@ -50,7 +49,7 @@ public class RowCountProjection extends SimpleProjection {
}
public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) throws HibernateException {
return getFunction( criteriaQuery ).render( ARGS, criteriaQuery.getFactory() )
return getFunction( criteriaQuery ).render( null, ARGS, criteriaQuery.getFactory() )
+ " as y" + position + '_';
}

View File

@ -32,26 +32,28 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.List;
import java.util.Iterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.AvgFunction;
import org.hibernate.dialect.function.CastFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardAnsiSqlAggregationFunctions;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.lock.*;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy;
import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.exception.SQLExceptionConverter;
import org.hibernate.exception.SQLStateConverter;
import org.hibernate.exception.ViolatedConstraintNameExtracter;
@ -63,9 +65,8 @@ import org.hibernate.persister.entity.Lockable;
import org.hibernate.sql.ANSICaseFragment;
import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.type.Type;
import org.hibernate.sql.JoinFragment;
import org.hibernate.util.ReflectHelper;
import org.hibernate.util.StringHelper;
@ -93,110 +94,11 @@ public abstract class Dialect {
public static final String QUOTE = "`\"[";
public static final String CLOSED_QUOTE = "`\"]";
// build the map of standard ANSI SQL aggregation functions ~~~~~~~~~~~~~~~
private static final Map STANDARD_AGGREGATE_FUNCTIONS = new HashMap();
static {
STANDARD_AGGREGATE_FUNCTIONS.put(
"count",
new StandardSQLFunction("count") {
public Type getReturnType(Type columnType, Mapping mapping) {
return Hibernate.LONG;
}
public String render(List args, SessionFactoryImplementor factory) {
if ( args.size() > 1 ) {
if ( "distinct".equalsIgnoreCase( args.get( 0 ).toString() ) ) {
return renderCountDistinct( args );
}
}
return super.render( args, factory );
}
private String renderCountDistinct(List args) {
StringBuffer buffer = new StringBuffer();
buffer.append( "count(distinct " );
String sep = "";
Iterator itr = args.iterator();
itr.next(); // intentionally skip first
while ( itr.hasNext() ) {
buffer.append( sep )
.append( itr.next() );
sep = ", ";
}
return buffer.append( ")" ).toString();
}
}
);
STANDARD_AGGREGATE_FUNCTIONS.put( "avg", new AvgFunction() );
STANDARD_AGGREGATE_FUNCTIONS.put( "max", new StandardSQLFunction("max") );
STANDARD_AGGREGATE_FUNCTIONS.put( "min", new StandardSQLFunction("min") );
STANDARD_AGGREGATE_FUNCTIONS.put(
"sum",
new StandardSQLFunction("sum") {
public Type getReturnType(Type columnType, Mapping mapping) {
//pre H3.2 behavior: super.getReturnType(ct, m);
int[] sqlTypes;
try {
sqlTypes = columnType.sqlTypes( mapping );
}
catch ( MappingException me ) {
throw new QueryException( me );
}
if ( sqlTypes.length != 1 ) {
throw new QueryException( "multi-column type in sum()" );
}
int sqlType = sqlTypes[0];
// First allow the actual type to control the return value; the underlying sqltype could
// actually be different
if ( columnType == Hibernate.BIG_INTEGER ) {
return Hibernate.BIG_INTEGER;
}
else if ( columnType == Hibernate.BIG_DECIMAL ) {
return Hibernate.BIG_DECIMAL;
}
else if ( columnType == Hibernate.LONG
|| columnType == Hibernate.SHORT
|| columnType == Hibernate.INTEGER ) {
return Hibernate.LONG;
}
else if ( columnType == Hibernate.FLOAT || columnType == Hibernate.DOUBLE) {
return Hibernate.DOUBLE;
}
// finally use the sqltype if == on Hibernate types did not find a match.
if ( sqlType == Types.NUMERIC ) {
return columnType; //because numeric can be anything
}
else if ( sqlType == Types.FLOAT
|| sqlType == Types.DOUBLE
|| sqlType == Types.DECIMAL
|| sqlType == Types.REAL) {
return Hibernate.DOUBLE;
}
else if ( sqlType == Types.BIGINT
|| sqlType == Types.INTEGER
|| sqlType == Types.SMALLINT
|| sqlType == Types.TINYINT ) {
return Hibernate.LONG;
}
else {
return columnType;
}
}
}
);
}
private final TypeNames typeNames = new TypeNames();
private final TypeNames hibernateTypeNames = new TypeNames();
private final Properties properties = new Properties();
private final Map sqlFunctions = new HashMap();
private final Map<String, SQLFunction> sqlFunctions = new HashMap<String, SQLFunction>();
private final Set sqlKeywords = new HashSet();
@ -204,7 +106,7 @@ public abstract class Dialect {
protected Dialect() {
log.info( "Using dialect: " + this );
sqlFunctions.putAll( STANDARD_AGGREGATE_FUNCTIONS );
StandardAnsiSqlAggregationFunctions.primeFunctionMap( sqlFunctions );
// standard sql92 functions (can be overridden by subclasses)
registerFunction( "substring", new SQLFunctionTemplate( Hibernate.STRING, "substring(?1, ?2, ?3)" ) );

View File

@ -43,13 +43,6 @@ import java.util.ArrayList;
* @author Steve Ebersole
*/
public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction {
/**
* {@inheritDoc}
*/
public final Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
/**
* {@inheritDoc}
*/
@ -67,7 +60,14 @@ public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction {
/**
* {@inheritDoc}
*/
public final String render(List args, SessionFactoryImplementor factory) throws QueryException {
public final Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
/**
* {@inheritDoc}
*/
public final String render(Type argumentType, 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:
//
@ -87,12 +87,12 @@ public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction {
if ( args.size() == 1 ) {
// we have the form: trim(trimSource)
// so we trim leading and trailing spaces
return resolveBothSpaceTrimFunction().render( args, factory ); // EARLY EXIT!!!!
return resolveBothSpaceTrimFunction().render( argumentType, 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!!!!
return resolveBothSpaceTrimFromFunction().render( argumentType, args, factory ); // EARLY EXIT!!!!
}
else {
// otherwise, a trim-specification and/or a trim-character
@ -145,24 +145,24 @@ public abstract class AbstractAnsiTrimEmulationFunction implements SQLFunction {
if ( trimCharacter.equals( "' '" ) ) {
if ( leading && trailing ) {
return resolveBothSpaceTrimFunction().render( argsToUse, factory );
return resolveBothSpaceTrimFunction().render( argumentType, argsToUse, factory );
}
else if ( leading ) {
return resolveLeadingSpaceTrimFunction().render( argsToUse, factory );
return resolveLeadingSpaceTrimFunction().render( argumentType, argsToUse, factory );
}
else {
return resolveTrailingSpaceTrimFunction().render( argsToUse, factory );
return resolveTrailingSpaceTrimFunction().render( argumentType, argsToUse, factory );
}
}
else {
if ( leading && trailing ) {
return resolveBothTrimFunction().render( argsToUse, factory );
return resolveBothTrimFunction().render( argumentType, argsToUse, factory );
}
else if ( leading ) {
return resolveLeadingTrimFunction().render( argsToUse, factory );
return resolveLeadingTrimFunction().render( argumentType, argsToUse, factory );
}
else {
return resolveTrailingTrimFunction().render( argsToUse, factory );
return resolveTrailingTrimFunction().render( argumentType, argsToUse, factory );
}
}
}

View File

@ -26,7 +26,7 @@ package org.hibernate.dialect.function;
import org.hibernate.engine.SessionFactoryImplementor;
/**
* Defines support for rendering according to ANSI SQL <tt>TRIM<//tt> function specification.
* Defines support for rendering according to ANSI SQL <tt>TRIM</tt> function specification.
*
* @author Steve Ebersole
*/

View File

@ -1,71 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. 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 Inc.
*
* 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 java.util.List;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.DoubleType;
import org.hibernate.type.Type;
/**
* The basic JPA spec compliant definition po<tt>AVG</tt> aggregation function.
*
* @author Steve Ebersole
*/
public class AvgFunction implements SQLFunction {
public final Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
int[] sqlTypes;
try {
sqlTypes = columnType.sqlTypes( mapping );
}
catch ( MappingException me ) {
throw new QueryException( me );
}
if ( sqlTypes.length != 1 ) {
throw new QueryException( "multiple-column type in avg()" );
}
return DoubleType.INSTANCE;
}
public final boolean hasArguments() {
return true;
}
public final boolean hasParenthesesIfNoArguments() {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
return "avg(" + args.get( 0 ) + ")";
}
@Override
public final String toString() {
return "avg";
}
}

View File

@ -23,32 +23,29 @@
*/
package org.hibernate.dialect.function;
import java.util.List;
import org.hibernate.QueryException;
import org.hibernate.engine.SessionFactoryImplementor;
import java.sql.Types;
/**
* Some databases strictly return the type of the of the aggregation value for <tt>AVG</tt> which is
* problematic in the case of averaging integers because the decimals will be dropped. The usual workaround
* is to cast the integer argument as some form of double/decimal.
* <p/>
* A downside to this approach is that we always wrap the avg() argument in a cast even though we may not need or want
* to. A more full-featured solution would be defining {@link SQLFunction} such that we render based on the first
* argument; essentially have {@link SQLFunction} describe the basic metadata about the function and merge the
* {@link SQLFunction#getReturnType} and {@link SQLFunction#render} methods into a
*
* @author Steve Ebersole
*/
public class AvgWithArgumentCastFunction extends AvgFunction {
private final TemplateRenderer renderer;
public class AvgWithArgumentCastFunction extends StandardAnsiSqlAggregationFunctions.AvgFunction {
private final String castType;
public AvgWithArgumentCastFunction(String castType) {
renderer = new TemplateRenderer( "avg(cast(?1 as " + castType + "))" );
this.castType = castType;
}
@Override
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
return renderer.render( args, factory );
protected String renderArgument(String argument, int firstArgumentJdbcType) {
if ( firstArgumentJdbcType == Types.DOUBLE || firstArgumentJdbcType == Types.FLOAT ) {
return argument;
}
else {
return "cast(" + argument + " as " + castType + ")";
}
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -30,7 +29,6 @@ import org.hibernate.QueryException;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
/**
* ANSI-SQL style <tt>cast(foo as type)</tt> where the type is
@ -38,11 +36,6 @@ import org.hibernate.type.TypeFactory;
* @author Gavin King
*/
public class CastFunction implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return columnType; //note there is a wierd implementation in the client side
}
public boolean hasArguments() {
return true;
}
@ -51,7 +44,11 @@ public class CastFunction implements SQLFunction {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return columnType; // this is really just a guess, unless the caller properly identifies the 'type' argument here
}
public String render(Type columnType, List args, SessionFactoryImplementor factory) throws QueryException {
if ( args.size()!=2 ) {
throw new QueryException("cast() requires two arguments");
}

View File

@ -37,11 +37,6 @@ import org.hibernate.type.Type;
* @author Nathan Moon
*/
public class CharIndexFunction implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
@ -50,7 +45,11 @@ public class CharIndexFunction implements SQLFunction {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.INTEGER;
}
public String render(Type columnType, List args, SessionFactoryImplementor factory) throws QueryException {
boolean threeArgs = args.size() > 2;
Object pattern = args.get(0);
Object string = args.get(1);

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -39,10 +38,6 @@ import org.hibernate.type.Type;
*/
public class ConvertFunction implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
public boolean hasArguments() {
return true;
}
@ -51,7 +46,11 @@ public class ConvertFunction implements SQLFunction {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
public String render(Type firstArgumentType, List args, SessionFactoryImplementor factory) throws QueryException {
if ( args.size() != 2 && args.size() != 3 ) {
throw new QueryException( "convert() requires two or three arguments" );
}

View File

@ -48,15 +48,6 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public class DerbyConcatFunction implements SQLFunction {
/**
* {@inheritDoc}
* <p/>
* Here we always return {@link Hibernate#STRING}.
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
/**
* {@inheritDoc}
* <p/>
@ -75,6 +66,15 @@ public class DerbyConcatFunction implements SQLFunction {
return true;
}
/**
* {@inheritDoc}
* <p/>
* Here we always return {@link Hibernate#STRING}.
*/
public Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
/**
* {@inheritDoc}
* <p/>
@ -82,10 +82,10 @@ public class DerbyConcatFunction implements SQLFunction {
* this method. The logic here says that if not all the incoming args are dynamic parameters
* (i.e. <tt>?</tt>) then we simply use the Derby concat operator (<tt>||</tt>) on the unchanged
* arg elements. However, if all the args are dynamic parameters, then we need to wrap the individual
* arg elements in <tt>cast</tt> function calls, use the concantenation operator on the <tt>cast</tt>
* arg elements in <tt>cast</tt> function calls, use the concatenation operator on the <tt>cast</tt>
* returns, and then wrap that whole thing in a call to the Derby <tt>varchar</tt> function.
*/
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public String render(Type argumentType, List args, SessionFactoryImplementor factory) throws QueryException {
boolean areAllArgsParams = true;
Iterator itr = args.iterator();
while ( itr.hasNext() ) {

View File

@ -51,10 +51,6 @@ public class NoArgSQLFunction implements SQLFunction {
this.name = name;
}
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return returnType;
}
public boolean hasArguments() {
return false;
}
@ -63,7 +59,11 @@ public class NoArgSQLFunction implements SQLFunction {
return hasParenthesesIfNoArguments;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
return returnType;
}
public String render(Type argumentType, List args, SessionFactoryImplementor factory) throws QueryException {
if ( args.size()>0 ) {
throw new QueryException("function takes no arguments: " + name);
}

View File

@ -32,16 +32,11 @@ import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
/**
* Emulation of <tt>coalesce()</tt> on Oracle, using multiple
* <tt>nvl()</tt> calls
* Emulation of <tt>coalesce()</tt> on Oracle, using multiple <tt>nvl()</tt> calls
*
* @author Gavin King
*/
public class NvlFunction implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return columnType;
}
public boolean hasArguments() {
return true;
}
@ -50,14 +45,20 @@ public class NvlFunction implements SQLFunction {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
return argumentType;
}
public String render(Type argumentType, List args, SessionFactoryImplementor factory) throws QueryException {
int lastIndex = args.size()-1;
Object last = args.remove(lastIndex);
if ( lastIndex==0 ) return last.toString();
if ( lastIndex==0 ) {
return last.toString();
}
Object secondLast = args.get(lastIndex-1);
String nvl = "nvl(" + secondLast + ", " + last + ")";
args.set(lastIndex-1, nvl);
return render(args, factory);
return render( argumentType, args, factory );
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -37,11 +36,6 @@ import org.hibernate.type.Type;
* @author Gavin King
*/
public class PositionSubstringFunction implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.INTEGER;
}
public boolean hasArguments() {
return true;
}
@ -50,7 +44,11 @@ public class PositionSubstringFunction implements SQLFunction {
return true;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
return Hibernate.INTEGER;
}
public String render(Type firstArgumentType, List args, SessionFactoryImplementor factory) throws QueryException {
boolean threeArgs = args.size() > 2;
Object pattern = args.get(0);
Object string = args.get(1);

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -40,6 +39,7 @@ import org.hibernate.type.Type;
* provide details required for processing of the function.
*
* @author David Channon
* @author Steve Ebersole
*/
public interface SQLFunction {
/**
@ -50,40 +50,41 @@ public interface SQLFunction {
public boolean hasArguments();
/**
* If there are no arguments, are parens required?
* If there are no arguments, are parentheses required?
*
* @return True if a no-arg call of this function requires parentheses.
*/
public boolean hasParenthesesIfNoArguments();
/**
* The return type of the function. May be either a concrete type which
* is preset, or variable depending upon the type of the first function
* argument.
* The return type of the function. May be either a concrete type which is preset, or variable depending upon
* the type of the first function argument.
* <p/>
* Note, the 'firstArgumentType' parameter should match the one passed into {@link #render}
*
* @param columnType the type of the first argument
* @param firstArgumentType The type of the first argument
* @param mapping The mapping source.
*
* @return The type to be expected as a return.
*
* @throws org.hibernate.QueryException Indicates an issue resolving the return type.
*
* @deprecated See http://opensource.atlassian.com/projects/hibernate/browse/HHH-5212
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException;
public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException;
/**
* Render the function call as SQL fragment.
* <p/>
* Note, the 'firstArgumentType' parameter should match the one passed into {@link #getReturnType}
*
* @param args The function arguments
* @param firstArgumentType The type of the first argument
* @param arguments The function arguments
* @param factory The SessionFactory
*
* @return The rendered function call
*
* @throws org.hibernate.QueryException Indicates a problem rendering the
* function call.
*
* @deprecated See http://opensource.atlassian.com/projects/hibernate/browse/HHH-5212
*/
public String render(List args, SessionFactoryImplementor factory) throws QueryException;
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException;
}

View File

@ -39,7 +39,6 @@ import java.util.List;
* parameters with '?' followed by parameter's index (first index is 1).
*
* @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
* @version <tt>$Revision: 6608 $</tt>
*/
public class SQLFunctionTemplate implements SQLFunction {
private final Type type;
@ -59,14 +58,14 @@ public class SQLFunctionTemplate implements SQLFunction {
/**
* {@inheritDoc}
*/
public String render(List args, SessionFactoryImplementor factory) {
public String render(Type argumentType, List args, SessionFactoryImplementor factory) {
return renderer.render( args, factory );
}
/**
* {@inheritDoc}
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
public Type getReturnType(Type argumentType, Mapping mapping) throws QueryException {
return type;
}

View File

@ -0,0 +1,205 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. 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 Inc.
*
* 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 java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.hibernate.Hibernate;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.engine.Mapping;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class StandardAnsiSqlAggregationFunctions {
/**
* Definition of a standard ANSI SQL compliant <tt>COUNT</tt> function
*/
public static class CountFunction extends StandardSQLFunction {
public static final CountFunction INSTANCE = new CountFunction();
public CountFunction() {
super( "count", Hibernate.LONG );
}
@Override
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) {
if ( arguments.size() > 1 ) {
if ( "distinct".equalsIgnoreCase( arguments.get( 0 ).toString() ) ) {
return renderCountDistinct( arguments );
}
}
return super.render( firstArgumentType, arguments, factory );
}
private String renderCountDistinct(List arguments) {
StringBuffer buffer = new StringBuffer();
buffer.append( "count(distinct " );
String sep = "";
Iterator itr = arguments.iterator();
itr.next(); // intentionally skip first
while ( itr.hasNext() ) {
buffer.append( sep )
.append( itr.next() );
sep = ", ";
}
return buffer.append( ")" ).toString();
}
}
/**
* Definition of a standard ANSI SQL compliant <tt>AVG</tt> function
*/
public static class AvgFunction extends StandardSQLFunction {
public static final AvgFunction INSTANCE = new AvgFunction();
public AvgFunction() {
super( "avg", Hibernate.DOUBLE );
}
@Override
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException {
int jdbcTypeCode = determineJdbcTypeCode( firstArgumentType, factory );
return render( jdbcTypeCode, arguments.get(0).toString(), factory );
}
protected final int determineJdbcTypeCode(Type firstArgumentType, SessionFactoryImplementor factory) throws QueryException {
try {
final int[] jdbcTypeCodes = firstArgumentType.sqlTypes( factory );
if ( jdbcTypeCodes.length != 1 ) {
throw new QueryException( "multiple-column type in avg()" );
}
return jdbcTypeCodes[0];
}
catch ( MappingException me ) {
throw new QueryException( me );
}
}
protected String render(int firstArgumentJdbcType, String argument, SessionFactoryImplementor factory) {
return "avg(" + renderArgument( argument, firstArgumentJdbcType ) + ")";
}
protected String renderArgument(String argument, int firstArgumentJdbcType) {
return argument;
}
}
public static class MaxFunction extends StandardSQLFunction {
public static final MaxFunction INSTANCE = new MaxFunction();
public MaxFunction() {
super( "max" );
}
}
public static class MinFunction extends StandardSQLFunction {
public static final MinFunction INSTANCE = new MinFunction();
public MinFunction() {
super( "min" );
}
}
public static class SumFunction extends StandardSQLFunction {
public static final SumFunction INSTANCE = new SumFunction();
public SumFunction() {
super( "sum" );
}
protected final int determineJdbcTypeCode(Type type, Mapping mapping) throws QueryException {
try {
final int[] jdbcTypeCodes = type.sqlTypes( mapping );
if ( jdbcTypeCodes.length != 1 ) {
throw new QueryException( "multiple-column type in sum()" );
}
return jdbcTypeCodes[0];
}
catch ( MappingException me ) {
throw new QueryException( me );
}
}
public Type getReturnType(Type firstArgumentType, Mapping mapping) {
final int jdbcType = determineJdbcTypeCode( firstArgumentType, mapping );
// First allow the actual type to control the return value; the underlying sqltype could
// actually be different
if ( firstArgumentType == Hibernate.BIG_INTEGER ) {
return Hibernate.BIG_INTEGER;
}
else if ( firstArgumentType == Hibernate.BIG_DECIMAL ) {
return Hibernate.BIG_DECIMAL;
}
else if ( firstArgumentType == Hibernate.LONG
|| firstArgumentType == Hibernate.SHORT
|| firstArgumentType == Hibernate.INTEGER ) {
return Hibernate.LONG;
}
else if ( firstArgumentType == Hibernate.FLOAT || firstArgumentType == Hibernate.DOUBLE) {
return Hibernate.DOUBLE;
}
// finally use the jdbcType if == on Hibernate types did not find a match.
//
// IMPL NOTE : we do not match on Types.NUMERIC because it could be either, so we fall-through to the
// first argument type
if ( jdbcType == Types.FLOAT
|| jdbcType == Types.DOUBLE
|| jdbcType == Types.DECIMAL
|| jdbcType == Types.REAL) {
return Hibernate.DOUBLE;
}
else if ( jdbcType == Types.BIGINT
|| jdbcType == Types.INTEGER
|| jdbcType == Types.SMALLINT
|| jdbcType == Types.TINYINT ) {
return Hibernate.LONG;
}
// as a last resort, return the type of the first argument
return firstArgumentType;
}
}
public static void primeFunctionMap(Map<String, SQLFunction> functionMap) {
functionMap.put( AvgFunction.INSTANCE.getName(), AvgFunction.INSTANCE );
functionMap.put( CountFunction.INSTANCE.getName(), CountFunction.INSTANCE );
functionMap.put( MaxFunction.INSTANCE.getName(), MaxFunction.INSTANCE );
functionMap.put( MinFunction.INSTANCE.getName(), MinFunction.INSTANCE );
functionMap.put( SumFunction.INSTANCE.getName(), SumFunction.INSTANCE );
}
}

View File

@ -45,8 +45,8 @@ public class StandardJDBCEscapeFunction extends StandardSQLFunction {
super( name, typeValue );
}
public String render(List args, SessionFactoryImplementor factory) {
return "{fn " + super.render( args, factory ) + "}";
public String render(Type argumentType, List args, SessionFactoryImplementor factory) {
return "{fn " + super.render( argumentType, args, factory ) + "}";
}
public String toString() {

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -40,7 +39,7 @@ import org.hibernate.type.Type;
*/
public class StandardSQLFunction implements SQLFunction {
private final String name;
private final Type type;
private final Type registeredType;
/**
* Construct a standard SQL function definition with a variable return type;
@ -60,11 +59,11 @@ public class StandardSQLFunction implements SQLFunction {
* Construct a standard SQL function definition with a static return type.
*
* @param name The name of the function.
* @param type The static return type.
* @param registeredType The static return type.
*/
public StandardSQLFunction(String name, Type type) {
public StandardSQLFunction(String name, Type registeredType) {
this.name = name;
this.type = type;
this.registeredType = registeredType;
}
/**
@ -83,16 +82,7 @@ public class StandardSQLFunction implements SQLFunction {
* not static.
*/
public Type getType() {
return type;
}
/**
* {@inheritDoc}
*/
public Type getReturnType(Type columnType, Mapping mapping) {
// return the concrete type, or the underlying type if a concrete type
// was not specified
return type == null ? columnType : type;
return registeredType;
}
/**
@ -112,12 +102,19 @@ public class StandardSQLFunction implements SQLFunction {
/**
* {@inheritDoc}
*/
public String render(List args, SessionFactoryImplementor factory) {
public Type getReturnType(Type firstArgumentType, Mapping mapping) {
return registeredType == null ? firstArgumentType : registeredType;
}
/**
* {@inheritDoc}
*/
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor sessionFactory) {
StringBuffer buf = new StringBuffer();
buf.append( name ).append( '(' );
for ( int i = 0; i < args.size(); i++ ) {
buf.append( args.get( i ) );
if ( i < args.size() - 1 ) {
for ( int i = 0; i < arguments.size(); i++ ) {
buf.append( arguments.get( i ) );
if ( i < arguments.size() - 1 ) {
buf.append( ", " );
}
}
@ -127,4 +124,5 @@ public class StandardSQLFunction implements SQLFunction {
public String toString() {
return name;
}
}

View File

@ -37,10 +37,6 @@ import org.hibernate.type.Type;
* @author Steve Ebersole
*/
public abstract class TrimFunctionTemplate implements SQLFunction {
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
public boolean hasArguments() {
return true;
}
@ -49,7 +45,11 @@ public abstract class TrimFunctionTemplate implements SQLFunction {
return false;
}
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type firstArgument, Mapping mapping) throws QueryException {
return Hibernate.STRING;
}
public String render(Type firstArgument, List args, SessionFactoryImplementor factory) throws QueryException {
final Options options = new Options();
final String trimSource;

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.dialect.function;
@ -32,8 +31,7 @@ import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
/**
* Support for slightly more general templating than {@link StandardSQLFunction},
* with an unlimited number of arguments.
* Support for slightly more general templating than {@link StandardSQLFunction}, with an unlimited number of arguments.
*
* @author Gavin King
*/
@ -41,20 +39,20 @@ public class VarArgsSQLFunction implements SQLFunction {
private final String begin;
private final String sep;
private final String end;
private final Type type;
private final Type registeredType;
/**
* Constructs a VarArgsSQLFunction instance with a 'static' return type. An example of a 'static'
* return type would be something like an <tt>UPPER</tt> function which is always returning
* a SQL VARCHAR and thus a string type.
*
* @param type The return type.
* @param registeredType The return type.
* @param begin The beginning of the function templating.
* @param sep The separator for each individual function argument.
* @param end The end of the function templating.
*/
public VarArgsSQLFunction(Type type, String begin, String sep, String end) {
this.type = type;
public VarArgsSQLFunction(Type registeredType, String begin, String sep, String end) {
this.registeredType = registeredType;
this.begin = begin;
this.sep = sep;
this.end = end;
@ -70,19 +68,12 @@ public class VarArgsSQLFunction implements SQLFunction {
* @param sep The separator for each individual function argument.
* @param end The end of the function templating.
*
* @see #getReturnType Specifically, the 'columnType' argument is the 'dynamic' type.
* @see #getReturnType Specifically, the 'firstArgumentType' argument is the 'dynamic' type.
*/
public VarArgsSQLFunction(String begin, String sep, String end) {
this( null, begin, sep, end );
}
/**
* {@inheritDoc}
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
return type == null ? columnType : type;
}
/**
* {@inheritDoc}
* <p/>
@ -104,11 +95,15 @@ public class VarArgsSQLFunction implements SQLFunction {
/**
* {@inheritDoc}
*/
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
return registeredType == null ? firstArgumentType : registeredType;
}
public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) {
StringBuffer buf = new StringBuffer().append( begin );
for ( int i = 0; i < args.size(); i++ ) {
buf.append( transformArgument( ( String ) args.get( i ) ) );
if ( i < args.size() - 1 ) {
for ( int i = 0; i < arguments.size(); i++ ) {
buf.append( transformArgument( ( String ) arguments.get( i ) ) );
if ( i < arguments.size() - 1 ) {
buf.append( sep );
}
}
@ -116,8 +111,8 @@ public class VarArgsSQLFunction implements SQLFunction {
}
/**
* Called from {@link #render} to allow applying a change or transformation to each individual
* argument.
* Called from {@link #render} to allow applying a change or transformation
* to each individual argument.
*
* @param argument The argument being processed.
* @return The transformed argument; may be the same, though should never be null.

View File

@ -33,6 +33,8 @@ import antlr.RecognitionException;
import antlr.collections.AST;
import org.hibernate.QueryException;
import org.hibernate.hql.ast.tree.FunctionNode;
import org.hibernate.hql.ast.tree.SqlNode;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;
import org.hibernate.param.ParameterSpecification;
import org.hibernate.dialect.function.SQLFunction;
@ -202,10 +204,11 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter {
super.endFunctionTemplate( node );
}
else {
final Type functionType = functionNode.getFirstArgumentType();
// this function has a registered SQLFunction -> redirect output and catch the arguments
FunctionArguments functionArguments = ( FunctionArguments ) writer;
writer = outputStack.removeFirst();
out( sqlFunction.render( functionArguments.getArgs(), sessionFactory ) );
out( sqlFunction.render( functionType, functionArguments.getArgs(), sessionFactory ) );
}
}

View File

@ -29,6 +29,7 @@ import org.hibernate.hql.ast.util.ColumnHelper;
import org.hibernate.type.Type;
import antlr.SemanticException;
import antlr.collections.AST;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -62,6 +63,20 @@ public class AggregateNode extends AbstractSelectExpression implements SelectExp
return sqlFunction;
}
public Type getFirstArgumentType() {
AST argument = getFirstChild();
while ( argument != null ) {
if ( argument instanceof SqlNode ) {
final Type type = ( (SqlNode) argument ).getDataType();
if ( type != null ) {
return type;
}
argument = argument.getNextSibling();
}
}
return null;
}
public Type getDataType() {
// Get the function return value type, based on the type of the first argument.
return getSessionFactoryHelper().findFunctionReturnType( getText(), resolveFunction(), getFirstChild() );

View File

@ -24,6 +24,7 @@
package org.hibernate.hql.ast.tree;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.type.Type;
/**
* Identifies a node which models a SQL function.
@ -32,4 +33,5 @@ import org.hibernate.dialect.function.SQLFunction;
*/
public interface FunctionNode {
public SQLFunction getSQLFunction();
public Type getFirstArgumentType();
}

View File

@ -291,7 +291,10 @@ public class IdentNode extends FromReferenceNode implements SelectExpression {
return fe.getDataType();
}
SQLFunction sf = getWalker().getSessionFactoryHelper().findSQLFunction( getText() );
return sf == null ? null : sf.getReturnType( null, null );
if ( sf != null ) {
return sf.getReturnType( null, getWalker().getSessionFactoryHelper().getFactory() );
}
return null;
}
public void setScalarColumnText(int i) throws SemanticException {

View File

@ -96,6 +96,20 @@ public class MethodNode extends AbstractSelectExpression implements SelectExpres
return function;
}
public Type getFirstArgumentType() {
AST argument = getFirstChild();
while ( argument != null ) {
if ( argument instanceof SqlNode ) {
final Type type = ( (SqlNode) argument ).getDataType();
if ( type != null ) {
return type;
}
argument = argument.getNextSibling();
}
}
return null;
}
private void dialectFunction(AST exprList) {
function = getSessionFactoryHelper().findSQLFunction( methodName );
if ( function != null ) {

View File

@ -46,7 +46,6 @@ import org.hibernate.type.AssociationType;
import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
import antlr.SemanticException;
import antlr.collections.AST;

View File

@ -142,9 +142,14 @@ public class SelectParser implements Parser {
}
}
else if ( COUNT_MODIFIERS.contains( lctoken ) ) {
if ( !ready || !aggregate ) throw new QueryException( token + " only allowed inside aggregate function in SELECT" );
if ( !ready || !aggregate ) {
throw new QueryException( token + " only allowed inside aggregate function in SELECT" );
}
q.appendScalarSelectToken( token );
if ( "*".equals( token ) ) q.addSelectScalar( getFunction( "count", q ).getReturnType( Hibernate.LONG, q.getFactory() ) ); //special case
if ( "*".equals( token ) ) {
// special case
q.addSelectScalar( getFunction( "count", q ).getReturnType( Hibernate.LONG, q.getFactory() ) );
}
}
else if ( getFunction( lctoken, q ) != null && token.equals( q.unalias( token ) ) ) {
// the name of an SQL function

View File

@ -137,7 +137,7 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
expressions.add( child.getText() );
child = child.getNextSibling();
}
final String text = function.render( expressions, context.getSessionFactory() );
final String text = function.render( null, expressions, context.getSessionFactory() );
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text );
}
}

View File

@ -66,7 +66,7 @@ public class ComponentTest extends FunctionalTestCase {
else {
List args = new ArrayList();
args.add( "dob" );
f.setFormula( yearFunction.render( args, null ) );
f.setFormula( yearFunction.render( Hibernate.INTEGER, args, null ) );
}
}

View File

@ -5,6 +5,7 @@ import java.util.ArrayList;
import junit.framework.Test;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.cfg.Mappings;
@ -40,7 +41,7 @@ public class CompositeElementTest extends FunctionalTestCase {
if ( lengthFunction != null ) {
ArrayList args = new ArrayList();
args.add( "bio" );
f.setFormula( lengthFunction.render( args, null ) );
f.setFormula( lengthFunction.render( Hibernate.INTEGER, args, null ) );
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2009, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. 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.
* distributed under license by Red Hat Inc.
*
* 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
@ -23,8 +23,8 @@
*/
package org.hibernate.test.dialect.function;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import junit.framework.TestCase;
@ -48,22 +48,22 @@ public class AnsiTrimEmulationFunctionTest extends TestCase {
final String expectedPostTrimSuffix = ",' ','-'),'${space}$',' ')";
// -> trim(LEADING '-' FROM a.column)
String rendered = function.render( argList( "LEADING", "'-'", "FROM", trimSource ), null );
String rendered = function.render( null, 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 );
rendered = function.render( null, 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 );
rendered = function.render( null, 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 );
rendered = function.render( null, argList( "'-'", "FROM", trimSource ), null );
expected = expectedPostTrimPrefix + "ltrim(rtrim(" + expectedTrimPrep + "))" + expectedPostTrimSuffix;
assertEquals( expected, rendered );
}
@ -82,88 +82,62 @@ public class AnsiTrimEmulationFunctionTest extends TestCase {
final String expectedPostTrimSuffix = ",' ','-'),'${space}$',' ')";
// -> trim(LEADING '-' FROM a.column)
String rendered = function.render( argList( "LEADING", "'-'", "FROM", trimSource ), null );
String rendered = function.render( null, 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 );
rendered = function.render( null, 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 );
rendered = function.render( null, 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 );
rendered = function.render( null, 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 );
String rendered = function.render( null, argList( trimSource ), null );
assertEquals( "ltrim(rtrim(a.column))", rendered );
// -> trim(FROM a.column)
rendered = function.render( argList( "FROM", trimSource ), null );
rendered = function.render( null, argList( "FROM", trimSource ), null );
assertEquals( "ltrim(rtrim(a.column))", rendered );
// -> trim(BOTH FROM a.column)
rendered = function.render( argList( "BOTH", "FROM", trimSource ), null );
rendered = function.render( null, argList( "BOTH", "FROM", trimSource ), null );
assertEquals( "ltrim(rtrim(a.column))", rendered );
// -> trim(BOTH ' ' FROM a.column)
rendered = function.render( argList( "BOTH", "' '", "FROM", trimSource ), null );
rendered = function.render( null, argList( "BOTH", "' '", "FROM", trimSource ), null );
assertEquals( "ltrim(rtrim(a.column))", rendered );
// -> trim(LEADING FROM a.column)
rendered = function.render( argList( "LEADING", "FROM", trimSource ), null );
rendered = function.render( null, argList( "LEADING", "FROM", trimSource ), null );
assertEquals( "ltrim(a.column)", rendered );
// -> trim(LEADING ' ' FROM a.column)
rendered = function.render( argList( "LEADING", "' '", "FROM", trimSource ), null );
rendered = function.render( null, argList( "LEADING", "' '", "FROM", trimSource ), null );
assertEquals( "ltrim(a.column)", rendered );
// -> trim(TRAILING FROM a.column)
rendered = function.render( argList( "TRAILING", "FROM", trimSource ), null );
rendered = function.render( null, argList( "TRAILING", "FROM", trimSource ), null );
assertEquals( "rtrim(a.column)", rendered );
// -> trim(TRAILING ' ' FROM a.column)
rendered = function.render( argList( "TRAILING", "' '", "FROM", trimSource ), null );
rendered = function.render( null, 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;
private List argList(String... args) {
return Arrays.asList( args );
}
}

View File

@ -682,7 +682,7 @@ public class HQLTest extends QueryTranslatorTestCase {
assertTranslation( "from Animal an where an.bodyWeight > abs(3*5)" );
SQLFunction concat = getSessionFactoryImplementor().getSqlFunctionRegistry().findSQLFunction( "concat");
List list = new ArrayList(); list.add("'fat'"); list.add("'skinny'");
assertTranslation( "from Animal an where an.description = " + concat.render(list, getSessionFactoryImplementor()) );
assertTranslation( "from Animal an where an.description = " + concat.render(Hibernate.STRING, list, getSessionFactoryImplementor()) );
}
public void testNotOrWhereClause() {