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.Mapping;
|
||||||
import org.hibernate.engine.SessionFactoryImplementor;
|
import org.hibernate.engine.SessionFactoryImplementor;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.dialect.function.VarArgsSQLFunction;
|
|
||||||
import org.hibernate.dialect.function.SQLFunction;
|
import org.hibernate.dialect.function.SQLFunction;
|
||||||
import org.hibernate.dialect.function.SQLFunctionTemplate;
|
import org.hibernate.dialect.function.SQLFunctionTemplate;
|
||||||
|
import org.hibernate.dialect.function.DerbyConcatFunction;
|
||||||
import org.hibernate.id.TableHiLoGenerator;
|
import org.hibernate.id.TableHiLoGenerator;
|
||||||
import org.hibernate.sql.CaseFragment;
|
import org.hibernate.sql.CaseFragment;
|
||||||
import org.hibernate.sql.DerbyCaseFragment;
|
import org.hibernate.sql.DerbyCaseFragment;
|
||||||
|
@ -41,18 +41,18 @@ import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Simon Johnston
|
|
||||||
*
|
|
||||||
* Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an
|
* Hibernate Dialect for Cloudscape 10 - aka Derby. This implements both an
|
||||||
* override for the identity column generator as well as for the case statement
|
* override for the identity column generator as well as for the case statement
|
||||||
* issue documented at:
|
* issue documented at:
|
||||||
* http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby
|
* http://www.jroller.com/comments/kenlars99/Weblog/cloudscape_soon_to_be_derby
|
||||||
|
*
|
||||||
|
* @author Simon Johnston
|
||||||
*/
|
*/
|
||||||
public class DerbyDialect extends DB2Dialect {
|
public class DerbyDialect extends DB2Dialect {
|
||||||
|
|
||||||
public DerbyDialect() {
|
public DerbyDialect() {
|
||||||
super();
|
super();
|
||||||
registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "(","||",")" ) );
|
registerFunction( "concat", new DerbyConcatFunction() );
|
||||||
registerFunction( "trim", new DerbyTrimFunctionEmulation() );
|
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;
|
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.
|
* with an unlimited number of arguments.
|
||||||
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
*/
|
*/
|
||||||
public class VarArgsSQLFunction implements SQLFunction {
|
public class VarArgsSQLFunction implements SQLFunction {
|
||||||
|
|
||||||
private final String begin;
|
private final String begin;
|
||||||
private final String sep;
|
private final String sep;
|
||||||
private final String end;
|
private final String end;
|
||||||
private final Type type;
|
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) {
|
public VarArgsSQLFunction(Type type, String begin, String sep, String end) {
|
||||||
this.begin = begin;
|
|
||||||
this.sep = sep;
|
|
||||||
this.end = end;
|
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
|
||||||
|
|
||||||
public VarArgsSQLFunction(String begin, String sep, String end) {
|
|
||||||
this.begin = begin;
|
this.begin = begin;
|
||||||
this.sep = sep;
|
this.sep = sep;
|
||||||
this.end = end;
|
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 {
|
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() {
|
public boolean hasArguments() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
* <p/>
|
||||||
|
* Always returns true here.
|
||||||
|
*/
|
||||||
public boolean hasParenthesesIfNoArguments() {
|
public boolean hasParenthesesIfNoArguments() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
|
public String render(List args, SessionFactoryImplementor factory) throws QueryException {
|
||||||
StringBuffer buf = new StringBuffer().append( begin );
|
StringBuffer buf = new StringBuffer().append( begin );
|
||||||
for ( int i = 0; i < args.size(); i++ ) {
|
for ( int i = 0; i < args.size(); i++ ) {
|
||||||
buf.append( args.get(i) );
|
buf.append( transformArgument( ( String ) args.get( i ) ) );
|
||||||
if (i<args.size()-1) buf.append(sep);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue