[hibernate-commits] Hibernate SVN: r15158 - in core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect: function and 1 other directory.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Sep 8 15:43:43 EDT 2008


Author: steve.ebersole at jboss.com
Date: 2008-09-08 15:43:43 -0400 (Mon, 08 Sep 2008)
New Revision: 15158

Added:
   core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java
Modified:
   core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
   core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
Log:
HHH-3424 : concat function on Derby

Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java	2008-09-08 19:43:09 UTC (rev 15157)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/DerbyDialect.java	2008-09-08 19:43:43 UTC (rev 15158)
@@ -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/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java	                        (rev 0)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/DerbyConcatFunction.java	2008-09-08 19:43:43 UTC (rev 15158)
@@ -0,0 +1,179 @@
+/*
+ * 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 String join(Iterator/*<String>*/ elements, StringTransformer elementTransformer, StringJoinTemplate template) {
+		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/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java	2008-09-08 19:43:09 UTC (rev 15157)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/dialect/function/VarArgsSQLFunction.java	2008-09-08 19:43:43 UTC (rev 15158)
@@ -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;
+	}
 }




More information about the hibernate-commits mailing list