diff --git a/core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/core/src/main/java/org/hibernate/dialect/DerbyDialect.java index e01fd190b5..62b8c57c1c 100755 --- a/core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -30,9 +30,9 @@ 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.SQLFunction; import org.hibernate.dialect.function.SQLFunctionTemplate; +import org.hibernate.dialect.function.DerbyConcatFunction; import org.hibernate.id.TableHiLoGenerator; import org.hibernate.sql.CaseFragment; import org.hibernate.sql.DerbyCaseFragment; @@ -41,18 +41,18 @@ import java.util.List; import java.util.ArrayList; /** - * @author Simon Johnston - * * Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an * override for the identity column generator as well as for the case statement * issue documented at: * http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby + * + * @author Simon Johnston */ public class DerbyDialect extends DB2Dialect { public DerbyDialect() { super(); - registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) ); + registerFunction( "concat", new DerbyConcatFunction() ); registerFunction( "trim", new DerbyTrimFunctionEmulation() ); } diff --git a/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java b/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java new file mode 100644 index 0000000000..8264f3d7e4 --- /dev/null +++ b/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java @@ -0,0 +1,180 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, 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 java.util.List; +import java.util.Iterator; + +import org.hibernate.Hibernate; +import org.hibernate.QueryException; +import org.hibernate.engine.Mapping; +import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.type.Type; + +/** + * A specialized concat() function definition in which:
    + *
  1. we translate to use the concat operator ('||')
  2. + *
  3. wrap dynamic parameters in CASTs to VARCHAR
  4. + *
+ *

+ * This last spec is to deal with a limitation on DB2 and variants (e.g. Derby) + * where dynamic parameters cannot be used in concatenation unless they are being + * concatenated with at least one non-dynamic operand. And even then, the rules + * are so convoluted as to what is allowed and when the CAST is needed and when + * it is not that we just go ahead and do the CASTing. + * + * @author Steve Ebersole + */ +public class DerbyConcatFunction implements SQLFunction { + /** + * {@inheritDoc} + *

+ * Here we always return {@link Hibernate#STRING}. + */ + public Type getReturnType(Type columnType, Mapping mapping) throws QueryException { + return Hibernate.STRING; + } + + /** + * {@inheritDoc} + *

+ * Here we always return true + */ + public boolean hasArguments() { + return true; + } + + /** + * {@inheritDoc} + *

+ * Here we always return true + */ + public boolean hasParenthesesIfNoArguments() { + return true; + } + + /** + * {@inheritDoc} + *

+ * Here's the meat.. The whole reason we have a separate impl for this for Derby is to re-define + * this method. The logic here says that if not all the incoming args are dynamic parameters + * (i.e. ?) then we simply use the Derby concat operator (||) on the unchanged + * arg elements. However, if all the args are dynamic parameters, then we need to wrap the individual + * arg elements in cast function calls, use the concantenation operator on the cast + * returns, and then wrap that whole thing in a call to the Derby varchar function. + */ + public String render(List args, SessionFactoryImplementor factory) throws QueryException { + boolean areAllArgsParams = true; + Iterator itr = args.iterator(); + while ( itr.hasNext() ) { + final String arg = ( String ) itr.next(); + if ( ! "?".equals( arg ) ) { + areAllArgsParams = false; + break; + } + } + + if ( areAllArgsParams ) { + return join( + args.iterator(), + new StringTransformer() { + public String transform(String string) { + return "cast( ? as varchar(32672) )"; + } + }, + new StringJoinTemplate() { + public String getBeginning() { + return "varchar( "; + } + public String getSeparator() { + return " || "; + } + public String getEnding() { + return " )"; + } + } + ); + } + else { + return join( + args.iterator(), + new StringTransformer() { + public String transform(String string) { + return string; + } + }, + new StringJoinTemplate() { + public String getBeginning() { + return "("; + } + public String getSeparator() { + return "||"; + } + public String getEnding() { + return ")"; + } + } + ); + } + } + + private static interface StringTransformer { + public String transform(String string); + } + + private static interface StringJoinTemplate { + /** + * Getter for property 'beginning'. + * + * @return Value for property 'beginning'. + */ + public String getBeginning(); + /** + * Getter for property 'separator'. + * + * @return Value for property 'separator'. + */ + public String getSeparator(); + /** + * Getter for property 'ending'. + * + * @return Value for property 'ending'. + */ + public String getEnding(); + } + + private static String join(Iterator/**/ elements, StringTransformer elementTransformer, StringJoinTemplate template) { + // todo : make this available via StringHelper? + StringBuffer buffer = new StringBuffer( template.getBeginning() ); + while ( elements.hasNext() ) { + final String element = ( String ) elements.next(); + buffer.append( elementTransformer.transform( element ) ); + if ( elements.hasNext() ) { + buffer.append( template.getSeparator() ); + } + } + return buffer.append( template.getEnding() ).toString(); + } +} diff --git a/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java b/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java index b8c89045c1..56bd282f24 100755 --- a/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java +++ b/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java @@ -32,50 +32,97 @@ import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.type.Type; /** - * Support for slightly more general templating than StandardSQLFunction, + * Support for slightly more general templating than {@link StandardSQLFunction}, * with an unlimited number of arguments. + * * @author Gavin King */ public class VarArgsSQLFunction implements SQLFunction { - private final String begin; private final String sep; private final String end; private final Type type; - + + /** + * Constructs a VarArgsSQLFunction instance with a 'static' return type. An example of a 'static' + * return type would be something like an UPPER function which is always returning + * a SQL VARCHAR and thus a string type. + * + * @param type 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.begin = begin; - this.sep = sep; - this.end = end; this.type = type; - } - - public VarArgsSQLFunction(String begin, String sep, String end) { this.begin = begin; this.sep = sep; this.end = end; - this.type = null; } + /** + * Constructs a VarArgsSQLFunction instance with a 'dynamic' return type. For a dynamic return type, + * the type of the arguments are used to resolve the type. An example of a function with a + * 'dynamic' return would be MAX or MIN which return a double or an integer etc + * based on the types of the arguments. + * + * @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. + * + * @see #getReturnType Specifically, the 'columnType' 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; + return type == null ? columnType : type; } + /** + * {@inheritDoc} + *

+ * Always returns true here. + */ public boolean hasArguments() { return true; } + /** + * {@inheritDoc} + *

+ * Always returns true here. + */ public boolean hasParenthesesIfNoArguments() { return true; } + /** + * {@inheritDoc} + */ public String render(List args, SessionFactoryImplementor factory) throws QueryException { - StringBuffer buf = new StringBuffer().append(begin); - for ( int i=0; i