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:
+ * - we translate to use the concat operator ('||')
+ * - wrap dynamic parameters in CASTs to VARCHAR
+ *
+ *
+ * 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