Author: steve.ebersole(a)jboss.com
Date: 2010-05-07 16:53:34 -0400 (Fri, 07 May 2010)
New Revision: 19411
Modified:
core/trunk/core/src/main/java/org/hibernate/mapping/Column.java
core/trunk/core/src/main/java/org/hibernate/sql/Template.java
core/trunk/core/src/test/java/org/hibernate/sql/TemplateTest.java
Log:
HHH-5135 - "Ambiguous column" exception thrown with columns having the same name
as a function registered with the dialect (e.g. to_date, floor)
Modified: core/trunk/core/src/main/java/org/hibernate/mapping/Column.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/mapping/Column.java 2010-05-07 18:07:24
UTC (rev 19410)
+++ core/trunk/core/src/main/java/org/hibernate/mapping/Column.java 2010-05-07 20:53:34
UTC (rev 19411)
@@ -61,7 +61,8 @@
private String customWrite;
private String customRead;
- public Column() { };
+ public Column() {
+ }
public Column(String columnName) {
setName(columnName);
@@ -262,12 +263,17 @@
}
public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry) {
- String expr = getReadExpr(dialect);
- return Template.renderWhereStringTemplate(expr, dialect, functionRegistry);
+ return hasCustomRead()
+ ? Template.renderWhereStringTemplate( customRead, dialect, functionRegistry )
+ : Template.TEMPLATE + '.' + getQuotedName( dialect );
}
+ public boolean hasCustomRead() {
+ return ( customRead != null && customRead.length() > 0 );
+ }
+
public String getReadExpr(Dialect dialect) {
- return ( customRead != null && customRead.length() > 0 ) ? customRead :
getQuotedName(dialect);
+ return hasCustomRead() ? customRead : getQuotedName( dialect );
}
public String getWriteExpr() {
Modified: core/trunk/core/src/main/java/org/hibernate/sql/Template.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/sql/Template.java 2010-05-07 18:07:24 UTC
(rev 19410)
+++ core/trunk/core/src/main/java/org/hibernate/sql/Template.java 2010-05-07 20:53:34 UTC
(rev 19411)
@@ -24,9 +24,13 @@
*/
package org.hibernate.sql;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.StringTokenizer;
+import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionRegistry;
@@ -43,9 +47,9 @@
*/
public final class Template {
- private static final java.util.Set KEYWORDS = new HashSet();
- private static final java.util.Set BEFORE_TABLE_KEYWORDS = new HashSet();
- private static final java.util.Set FUNCTION_KEYWORDS = new HashSet();
+ private static final Set<String> KEYWORDS = new HashSet<String>();
+ private static final Set<String> BEFORE_TABLE_KEYWORDS = new
HashSet<String>();
+ private static final Set<String> FUNCTION_KEYWORDS = new HashSet<String>();
static {
KEYWORDS.add("and");
KEYWORDS.add("or");
@@ -109,13 +113,14 @@
*
* @deprecated Only intended for annotations usage; use {@link
#renderWhereStringTemplate(String, String, Dialect, SQLFunctionRegistry)} instead
*/
+ @SuppressWarnings({ "JavaDoc" })
public static String renderWhereStringTemplate(String sqlWhereString, String
placeholder, Dialect dialect) {
return renderWhereStringTemplate( sqlWhereString, placeholder, dialect, new
SQLFunctionRegistry( dialect, java.util.Collections.EMPTY_MAP ) );
}
/**
- * Takes the where condition provided in the mapping attribute and interpolates the
alias.
- * Handles subselects, quoted identifiers, quoted strings, expressions, SQL functions,
+ * Takes the where condition provided in the mapping attribute and interpolates the
alias.
+ * Handles sub-selects, quoted identifiers, quoted strings, expressions, SQL functions,
* named parameters.
*
* @param sqlWhereString The string into which to interpolate the placeholder value
@@ -125,51 +130,55 @@
* @return The rendered sql fragment
*/
public static String renderWhereStringTemplate(String sqlWhereString, String
placeholder, Dialect dialect, SQLFunctionRegistry functionRegistry ) {
- //TODO: make this a bit nicer
+
+ // IMPL NOTE : The basic process here is to tokenize the incoming string and to iterate
over each token
+ // in turn. As we process each token, we set a series of flags used to indicate the
type of context in
+ // which the tokens occur. Depending on the state of those flags we decide whether
we need to qualify
+ // identifier references.
+
String symbols = new StringBuffer()
- .append("=><!+-*/()',|&`")
- .append(StringHelper.WHITESPACE)
- .append( dialect.openQuote() )
- .append( dialect.closeQuote() )
- .toString();
- StringTokenizer tokens = new StringTokenizer(sqlWhereString, symbols, true);
-
+ .append( "=><!+-*/()',|&`" )
+ .append( StringHelper.WHITESPACE )
+ .append( dialect.openQuote() )
+ .append( dialect.closeQuote() )
+ .toString();
+ StringTokenizer tokens = new StringTokenizer( sqlWhereString, symbols, true );
StringBuffer result = new StringBuffer();
+
boolean quoted = false;
boolean quotedIdentifier = false;
boolean beforeTable = false;
boolean inFromClause = false;
boolean afterFromTable = false;
-
+
boolean hasMore = tokens.hasMoreTokens();
String nextToken = hasMore ? tokens.nextToken() : null;
- while (hasMore) {
+ while ( hasMore ) {
String token = nextToken;
String lcToken = token.toLowerCase();
hasMore = tokens.hasMoreTokens();
nextToken = hasMore ? tokens.nextToken() : null;
-
+
boolean isQuoteCharacter = false;
-
+
if ( !quotedIdentifier && "'".equals(token) ) {
quoted = !quoted;
isQuoteCharacter = true;
}
-
+
if ( !quoted ) {
-
boolean isOpenQuote;
if ( "`".equals(token) ) {
isOpenQuote = !quotedIdentifier;
- token = lcToken = isOpenQuote ?
- new Character( dialect.openQuote() ).toString() :
- new Character( dialect.closeQuote() ).toString();
- quotedIdentifier = isOpenQuote;
+ token = lcToken = isOpenQuote
+ ? Character.toString( dialect.openQuote() )
+ : Character.toString( dialect.closeQuote() );
+ quotedIdentifier = isOpenQuote;
isQuoteCharacter = true;
}
else if ( !quotedIdentifier && ( dialect.openQuote()==token.charAt(0) ) ) {
isOpenQuote = true;
- quotedIdentifier = true;
+ quotedIdentifier = true;
isQuoteCharacter = true;
}
else if ( quotedIdentifier && ( dialect.closeQuote()==token.charAt(0) ) ) {
@@ -180,40 +189,120 @@
else {
isOpenQuote = false;
}
-
- if (isOpenQuote) {
- result.append(placeholder).append('.');
+
+ if ( isOpenQuote ) {
+ result.append( placeholder ).append( '.' );
}
-
}
-
- boolean quotedOrWhitespace = quoted ||
- quotedIdentifier ||
- isQuoteCharacter ||
- Character.isWhitespace( token.charAt(0) );
-
- if (quotedOrWhitespace) {
- result.append(token);
+
+ // Special processing for ANSI SQL EXTRACT function
+ if ( "extract".equals( lcToken ) && "(".equals( nextToken
) ) {
+ final String field = extractUntil( tokens, "from" );
+ final String source = renderWhereStringTemplate(
+ extractUntil( tokens, ")" ),
+ placeholder,
+ dialect,
+ functionRegistry
+ );
+ result.append( "extract(" ).append( field ).append( " from "
).append( source ).append( ')' );
+
+ hasMore = tokens.hasMoreTokens();
+ nextToken = hasMore ? tokens.nextToken() : null;
+
+ continue;
}
- else if (beforeTable) {
- result.append(token);
+
+ // Special processing for ANSI SQL TRIM function
+ if ( "trim".equals( lcToken ) && "(".equals( nextToken ) )
{
+ List<String> operands = new ArrayList<String>();
+ StringBuilder builder = new StringBuilder();
+
+ boolean hasMoreOperands = true;
+ String operandToken = tokens.nextToken();
+ boolean quotedOperand = false;
+ while ( hasMoreOperands ) {
+ final boolean isQuote = "'".equals( operandToken );
+ if ( isQuote ) {
+ quotedOperand = !quotedOperand;
+ if ( !quotedOperand ) {
+ operands.add( builder.append( '\'' ).toString() );
+ builder.setLength( 0 );
+ }
+ else {
+ builder.append( '\'' );
+ }
+ }
+ else if ( quotedOperand ) {
+ builder.append( operandToken );
+ }
+ else if ( operandToken.length() == 1 && Character.isWhitespace(
operandToken.charAt( 0 ) ) ) {
+ // do nothing
+ }
+ else {
+ operands.add( operandToken );
+ }
+ operandToken = tokens.nextToken();
+ hasMoreOperands = tokens.hasMoreTokens() && ! ")".equals(
operandToken );
+ }
+
+ TrimOperands trimOperands = new TrimOperands( operands );
+ result.append( "trim(" );
+ if ( trimOperands.trimSpec != null ) {
+ result.append( trimOperands.trimSpec ).append( ' ' );
+ }
+ if ( trimOperands.trimChar != null ) {
+ if ( trimOperands.trimChar.startsWith( "'" ) &&
trimOperands.trimChar.endsWith( "'" ) ) {
+ result.append( trimOperands.trimChar );
+ }
+ else {
+ result.append(
+ renderWhereStringTemplate( trimOperands.trimSpec, placeholder, dialect,
functionRegistry )
+ );
+ }
+ result.append( ' ' );
+ }
+ if ( trimOperands.from != null ) {
+ result.append( trimOperands.from ).append( ' ' );
+ }
+ else if ( trimOperands.trimSpec != null || trimOperands.trimChar != null ) {
+ // I think ANSI SQL says that the 'from' is not optional if either trim-spec
or trim-char are specified
+ result.append( "from " );
+ }
+
+ result.append( renderWhereStringTemplate( trimOperands.trimSource, placeholder,
dialect, functionRegistry ) )
+ .append( ')' );
+
+ hasMore = tokens.hasMoreTokens();
+ nextToken = hasMore ? tokens.nextToken() : null;
+
+ continue;
+ }
+
+ boolean quotedOrWhitespace = quoted || quotedIdentifier || isQuoteCharacter
+ || Character.isWhitespace( token.charAt(0) );
+
+ if ( quotedOrWhitespace ) {
+ result.append( token );
+ }
+ else if ( beforeTable ) {
+ result.append( token );
beforeTable = false;
afterFromTable = true;
}
- else if (afterFromTable) {
- if ( !"as".equals(lcToken) ) afterFromTable = false;
+ else if ( afterFromTable ) {
+ if ( !"as".equals(lcToken) ) {
+ afterFromTable = false;
+ }
result.append(token);
}
else if ( isNamedParameter(token) ) {
result.append(token);
}
- else if (
- isIdentifier(token, dialect) &&
- !isFunctionOrKeyword(lcToken, nextToken, dialect , functionRegistry)
- ) {
+ else if ( isIdentifier(token, dialect)
+ && !isFunctionOrKeyword(lcToken, nextToken, dialect , functionRegistry) ) {
result.append(placeholder)
- .append('.')
- .append( dialect.quote(token) );
+ .append('.')
+ .append( dialect.quote(token) );
}
else {
if ( BEFORE_TABLE_KEYWORDS.contains(lcToken) ) {
@@ -225,19 +314,313 @@
}
result.append(token);
}
-
- if ( //Yuck:
- inFromClause &&
- KEYWORDS.contains(lcToken) && //"as" is not in KEYWORDS
- !BEFORE_TABLE_KEYWORDS.contains(lcToken)
- ) {
+
+ //Yuck:
+ if ( inFromClause
+ && KEYWORDS.contains( lcToken ) //"as" is not in KEYWORDS
+ && !BEFORE_TABLE_KEYWORDS.contains( lcToken ) ) {
inFromClause = false;
}
+ }
- }
return result.toString();
}
+// /**
+// * Takes the where condition provided in the mapping attribute and interpolates the
alias.
+// * Handles sub-selects, quoted identifiers, quoted strings, expressions, SQL
functions,
+// * named parameters.
+// *
+// * @param sqlWhereString The string into which to interpolate the placeholder value
+// * @param placeholder The value to be interpolated into the the sqlWhereString
+// * @param dialect The dialect to apply
+// * @param functionRegistry The registry of all sql functions
+// *
+// * @return The rendered sql fragment
+// */
+// public static String renderWhereStringTemplate(
+// String sqlWhereString,
+// String placeholder,
+// Dialect dialect,
+// SQLFunctionRegistry functionRegistry) {
+//
+// // IMPL NOTE : The basic process here is to tokenize the incoming string and to
iterate over each token
+// // in turn. As we process each token, we set a series of flags used to indicate the
type of context in
+// // which the tokens occur. Depending on the state of those flags we decide whether
we need to qualify
+// // identifier references.
+//
+// final String dialectOpenQuote = Character.toString( dialect.openQuote() );
+// final String dialectCloseQuote = Character.toString( dialect.closeQuote() );
+//
+// String symbols = new StringBuilder()
+// .append( "=><!+-*/()',|&`" )
+// .append( StringHelper.WHITESPACE )
+// .append( dialect.openQuote() )
+// .append( dialect.closeQuote() )
+// .toString();
+// StringTokenizer tokens = new StringTokenizer( sqlWhereString, symbols, true );
+// ProcessingState state = new ProcessingState();
+//
+// StringBuilder quotedBuffer = new StringBuilder();
+// StringBuilder result = new StringBuilder();
+//
+// boolean hasMore = tokens.hasMoreTokens();
+// String nextToken = hasMore ? tokens.nextToken() : null;
+// while ( hasMore ) {
+// String token = nextToken;
+// String lcToken = token.toLowerCase();
+// hasMore = tokens.hasMoreTokens();
+// nextToken = hasMore ? tokens.nextToken() : null;
+//
+// // First, determine quoting which might be based on either:
+// // 1) back-tick
+// // 2) single quote (ANSI SQL standard)
+// // 3) or dialect defined quote character(s)
+// QuotingCharacterDisposition quotingCharacterDisposition =
QuotingCharacterDisposition.NONE;
+// if ( "`".equals( token ) ) {
+// state.quoted = !state.quoted;
+// quotingCharacterDisposition = state.quoted
+// ? QuotingCharacterDisposition.OPEN
+// : QuotingCharacterDisposition.CLOSE;
+// // replace token with the appropriate dialect quoting char
+// token = lcToken = ( quotingCharacterDisposition == QuotingCharacterDisposition.OPEN
)
+// ? dialectOpenQuote
+// : dialectCloseQuote;
+// }
+// else if ( "'".equals( token ) ) {
+// state.quoted = !state.quoted;
+// quotingCharacterDisposition = state.quoted
+// ? QuotingCharacterDisposition.OPEN
+// : QuotingCharacterDisposition.CLOSE;
+// }
+// else if ( !state.quoted && dialectOpenQuote.equals( token ) ) {
+// state.quoted = true;
+// quotingCharacterDisposition = QuotingCharacterDisposition.OPEN;
+// }
+// else if ( state.quoted && dialectCloseQuote.equals( token ) ) {
+// state.quoted = false;
+// quotingCharacterDisposition = QuotingCharacterDisposition.CLOSE;
+// }
+//
+// if ( state.quoted ) {
+// quotedBuffer.append( token );
+// continue;
+// }
+//
+// // if we were previously processing quoted state and just encountered the close
quote, then handle that
+// // quoted text
+// if ( quotingCharacterDisposition == QuotingCharacterDisposition.CLOSE ) {
+// token = quotedBuffer.toString();
+// quotedBuffer.setLength( 0 );
+// result.append( placeholder ).append( '.' )
+// .append( dialectOpenQuote ).append( token ).append( dialectCloseQuote );
+// continue;
+// }
+//
+// // Special processing for ANSI SQL EXTRACT function
+// if ( "extract".equals( lcToken ) && "(".equals(
nextToken ) ) {
+// final String field = extractUntil( tokens, "from" );
+// final String source = renderWhereStringTemplate(
+// extractUntil( tokens, ")" ),
+// placeholder,
+// dialect,
+// functionRegistry
+// );
+// result.append( "extract(" ).append( field ).append( " from "
).append( source ).append( ')' );
+//
+// hasMore = tokens.hasMoreTokens();
+// nextToken = hasMore ? tokens.nextToken() : null;
+//
+// continue;
+// }
+//
+// // Special processing for ANSI SQL TRIM function
+// if ( "trim".equals( lcToken ) && "(".equals( nextToken )
) {
+// List<String> operands = new ArrayList<String>();
+// StringBuilder builder = new StringBuilder();
+//
+// boolean hasMoreOperands = true;
+// String operandToken = tokens.nextToken();
+// boolean quoted = false;
+// while ( hasMoreOperands ) {
+// final boolean isQuote = "'".equals( operandToken );
+// if ( isQuote ) {
+// quoted = !quoted;
+// if ( !quoted ) {
+// operands.add( builder.append( '\'' ).toString() );
+// builder.setLength( 0 );
+// }
+// else {
+// builder.append( '\'' );
+// }
+// }
+// else if ( quoted ) {
+// builder.append( operandToken );
+// }
+// else if ( operandToken.length() == 1 && Character.isWhitespace(
operandToken.charAt( 0 ) ) ) {
+// // do nothing
+// }
+// else {
+// operands.add( operandToken );
+// }
+// operandToken = tokens.nextToken();
+// hasMoreOperands = tokens.hasMoreTokens() && ! ")".equals(
operandToken );
+// }
+//
+// TrimOperands trimOperands = new TrimOperands( operands );
+// result.append( "trim(" );
+// if ( trimOperands.trimSpec != null ) {
+// result.append( trimOperands.trimSpec ).append( ' ' );
+// }
+// if ( trimOperands.trimChar != null ) {
+// if ( trimOperands.trimChar.startsWith( "'" ) &&
trimOperands.trimChar.endsWith( "'" ) ) {
+// result.append( trimOperands.trimChar );
+// }
+// else {
+// result.append(
+// renderWhereStringTemplate( trimOperands.trimSpec, placeholder, dialect,
functionRegistry )
+// );
+// }
+// result.append( ' ' );
+// }
+// if ( trimOperands.from != null ) {
+// result.append( trimOperands.from ).append( ' ' );
+// }
+// else if ( trimOperands.trimSpec != null || trimOperands.trimChar != null ) {
+// // I think ANSI SQL says that the 'from' is not optional if either
trim-spec or trim-char are specified
+// result.append( "from " );
+// }
+//
+// result.append( renderWhereStringTemplate( trimOperands.trimSource, placeholder,
dialect, functionRegistry ) )
+// .append( ')' );
+//
+// hasMore = tokens.hasMoreTokens();
+// nextToken = hasMore ? tokens.nextToken() : null;
+//
+// continue;
+// }
+//
+//
+// // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+// if ( Character.isWhitespace( token.charAt( 0 ) ) ) {
+// result.append( token );
+// }
+// else if ( state.beforeTable ) {
+// result.append( token );
+// state.beforeTable = false;
+// state.afterFromTable = true;
+// }
+// else if ( state.afterFromTable ) {
+// if ( !"as".equals(lcToken) ) {
+// state.afterFromTable = false;
+// }
+// result.append(token);
+// }
+// else if ( isNamedParameter(token) ) {
+// result.append(token);
+// }
+// else if ( isIdentifier(token, dialect)
+// && !isFunctionOrKeyword(lcToken, nextToken, dialect , functionRegistry) )
{
+// result.append(placeholder)
+// .append('.')
+// .append( dialect.quote(token) );
+// }
+// else {
+// if ( BEFORE_TABLE_KEYWORDS.contains(lcToken) ) {
+// state.beforeTable = true;
+// state.inFromClause = true;
+// }
+// else if ( state.inFromClause && ",".equals(lcToken) ) {
+// state.beforeTable = true;
+// }
+// result.append(token);
+// }
+//
+// //Yuck:
+// if ( state.inFromClause
+// && KEYWORDS.contains( lcToken ) //"as" is not in KEYWORDS
+// && !BEFORE_TABLE_KEYWORDS.contains( lcToken ) ) {
+// state.inFromClause = false;
+// }
+// }
+//
+// return result.toString();
+// }
+//
+// private static class ProcessingState {
+// boolean quoted = false;
+// boolean quotedIdentifier = false;
+// boolean beforeTable = false;
+// boolean inFromClause = false;
+// boolean afterFromTable = false;
+// }
+//
+// private static enum QuotingCharacterDisposition { NONE, OPEN, CLOSE }
+
+ private static class TrimOperands {
+ private final String trimSpec;
+ private final String trimChar;
+ private final String from;
+ private final String trimSource;
+
+ private TrimOperands(List<String> operands) {
+ if ( operands.size() == 1 ) {
+ trimSpec = null;
+ trimChar = null;
+ from = null;
+ trimSource = operands.get(0);
+ }
+ else if ( operands.size() == 4 ) {
+ trimSpec = operands.get(0);
+ trimChar = operands.get(1);
+ from = operands.get(2);
+ trimSource = operands.get(3);
+ }
+ else {
+ if ( operands.size() < 1 || operands.size() > 4 ) {
+ throw new HibernateException( "Unexpected number of trim function operands :
" + operands.size() );
+ }
+
+ // trim-source will always be the last operand
+ trimSource = operands.get( operands.size() - 1 );
+
+ // ANSI SQL says that more than one operand means that the FROM is required
+ if ( ! "from".equals( operands.get( operands.size() - 2 ) ) ) {
+ throw new HibernateException( "Expecting FROM, found : " + operands.get(
operands.size() - 2 ) );
+ }
+ from = operands.get( operands.size() - 2 );
+
+ // trim-spec, if there is one will always be the first operand
+ if ( "leading".equalsIgnoreCase( operands.get(0) )
+ || "trailing".equalsIgnoreCase( operands.get(0) )
+ || "both".equalsIgnoreCase( operands.get(0) ) ) {
+ trimSpec = operands.get(0);
+ trimChar = null;
+ }
+ else {
+ trimSpec = null;
+ if ( operands.size() - 2 == 0 ) {
+ trimChar = null;
+ }
+ else {
+ trimChar = operands.get( 0 );
+ }
+ }
+ }
+ }
+ }
+
+ private static String extractUntil(StringTokenizer tokens, String delimiter) {
+ StringBuilder valueBuilder = new StringBuilder();
+ String token = tokens.nextToken();
+ while ( ! delimiter.equalsIgnoreCase( token ) ) {
+ valueBuilder.append( token );
+ token = tokens.nextToken();
+ }
+ return valueBuilder.toString().trim();
+ }
+
public static class NoOpColumnMapper implements ColumnMapper {
public static final NoOpColumnMapper INSTANCE = new NoOpColumnMapper();
public String[] map(String reference) {
@@ -254,8 +637,8 @@
* @param functionRegistry The SQL function registry
*
* @return The rendered <tt>ORDER BY</tt> template.
- *
- * @see
#renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)
+ *
+ * @deprecated Use {@link
#renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)}
instead
*/
public static String renderOrderByStringTemplate(
String orderByFragment,
@@ -348,5 +731,4 @@
);
}
-
}
Modified: core/trunk/core/src/test/java/org/hibernate/sql/TemplateTest.java
===================================================================
--- core/trunk/core/src/test/java/org/hibernate/sql/TemplateTest.java 2010-05-07 18:07:24
UTC (rev 19410)
+++ core/trunk/core/src/test/java/org/hibernate/sql/TemplateTest.java 2010-05-07 20:53:34
UTC (rev 19411)
@@ -85,6 +85,43 @@
private static final SQLFunctionRegistry FUNCTION_REGISTRY = new SQLFunctionRegistry(
DIALECT, Collections.EMPTY_MAP );
+ public void testSqlExtractFunction() {
+ String fragment = "extract( year from col )";
+ String template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE,
DIALECT, FUNCTION_REGISTRY );
+
+ assertEquals( "extract(year from " + Template.TEMPLATE + ".col)",
template );
+ }
+
+ public void testSqlTrimFunction() {
+ String fragment = "trim( col )";
+ String template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE,
DIALECT, FUNCTION_REGISTRY );
+ assertEquals( "trim(" + Template.TEMPLATE + ".col)", template );
+
+ fragment = "trim( from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim(from " + Template.TEMPLATE + ".col)", template
);
+
+ fragment = "trim( both from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim(both from " + Template.TEMPLATE + ".col)",
template );
+
+ fragment = "trim( leading from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim(leading from " + Template.TEMPLATE + ".col)",
template );
+
+ fragment = "trim( TRAILING from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim(TRAILING from " + Template.TEMPLATE + ".col)",
template );
+
+ fragment = "trim( 'b' from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim('b' from " + Template.TEMPLATE +
".col)", template );
+
+ fragment = "trim( both 'b' from col )";
+ template = Template.renderWhereStringTemplate( fragment, Template.TEMPLATE, DIALECT,
FUNCTION_REGISTRY );
+ assertEquals( "trim(both 'b' from " + Template.TEMPLATE +
".col)", template );
+ }
+
public void testSQLReferences() {
String fragment = "sql asc, sql desc";
String template = doStandardRendering( fragment );