Author: steve.ebersole(a)jboss.com
Date: 2008-09-08 15:43:09 -0400 (Mon, 08 Sep 2008)
New Revision: 15157
Added:
core/trunk/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java
Modified:
core/trunk/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
core/trunk/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
Log:
HHH-3424 : concat function on Derby
Modified: core/trunk/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/DerbyDialect.java 2008-09-08
14:07:27 UTC (rev 15156)
+++ core/trunk/core/src/main/java/org/hibernate/dialect/DerbyDialect.java 2008-09-08
19:43:09 UTC (rev 15157)
@@ -30,9 +30,9 @@
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.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() );
}
Added:
core/trunk/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java
(rev 0)
+++
core/trunk/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java 2008-09-08
19:43:09 UTC (rev 15157)
@@ -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:<ol>
+ * <li>we translate to use the concat operator ('||')</li>
+ * <li>wrap dynamic parameters in CASTs to VARCHAR</li>
+ * </ol>
+ * <p/>
+ * 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}
+ * <p/>
+ * Here we always return {@link Hibernate#STRING}.
+ */
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Here we always return <tt>true</tt>
+ */
+ public boolean hasArguments() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * Here we always return <tt>true</tt>
+ */
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * 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. <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>
+ * 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
{
+ 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/*<String>*/ 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();
+ }
+}
Modified:
core/trunk/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
===================================================================
---
core/trunk/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java 2008-09-08
14:07:27 UTC (rev 15156)
+++
core/trunk/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java 2008-09-08
19:43:09 UTC (rev 15157)
@@ -32,50 +32,97 @@
import org.hibernate.type.Type;
/**
- * Support for slightly more general templating than
<tt>StandardSQLFunction</tt>,
+ * 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 <tt>UPPER</tt> 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.type = type;
this.begin = begin;
this.sep = sep;
this.end = end;
- this.type = type;
}
+ /**
+ * 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 <tt>MAX</tt> or
<tt>MIN</tt> 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.begin = begin;
- this.sep = sep;
- this.end = end;
- this.type = null;
+ 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}
+ * <p/>
+ * Always returns true here.
+ */
public boolean hasArguments() {
return true;
}
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * 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<args.size(); i++ ) {
- buf.append( args.get(i) );
- if (i<args.size()-1) buf.append(sep);
+ 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 ) {
+ buf.append( sep );
+ }
}
- return buf.append(end).toString();
+ return buf.append( end ).toString();
}
+ /**
+ * 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.
+ */
+ protected String transformArgument(String argument) {
+ return argument;
+ }
}