HHH-3424 : concat function on Derby
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15157 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
343e727e4b
commit
c6c0a826a5
|
@ -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() );
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -32,50 +32,97 @@ import org.hibernate.engine.SessionFactoryImplementor;
|
|||
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.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 <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( null, begin, sep, end );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
|
||||
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);
|
||||
buf.append( transformArgument( ( String ) args.get( i ) ) );
|
||||
if ( i < args.size() - 1 ) {
|
||||
buf.append( sep );
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue