HHH-2802 : order-by mapping -> support for property names

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@15359 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2008-10-20 17:57:44 +00:00
parent 13ddb15f29
commit 538982e8e6
20 changed files with 1677 additions and 110 deletions

View File

@ -77,7 +77,7 @@
<artifactId>antlr-maven-plugin</artifactId> <artifactId>antlr-maven-plugin</artifactId>
<version>${antlrPluginVersion}</version> <version>${antlrPluginVersion}</version>
<configuration> <configuration>
<grammars>hql.g,hql-sql.g,sql-gen.g</grammars> <grammars>hql.g,hql-sql.g,sql-gen.g,order-by.g,order-by-render.g</grammars>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>

View File

@ -0,0 +1,92 @@
header
{
/*
* 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.sql.ordering.antlr;
}
/**
* Antlr grammar for rendering <tt>ORDER_BY</tt> trees as described by the {@link OrderByFragmentParser}
* @author Steve Ebersole
*/
class GeneratedOrderByFragmentRenderer extends TreeParser;
options {
importVocab=OrderByTemplate;
buildAST=false;
}
{
// the buffer to which we write the resulting SQL.
private StringBuffer buffer = new StringBuffer();
protected void out(String text) {
buffer.append( text );
}
protected void out(AST ast) {
buffer.append( ast.getText() );
}
/*package*/ String getRenderedFragment() {
return buffer.toString();
}
}
orderByFragment
: #(
ORDER_BY sortSpecification ( {out(", ");} sortSpecification)*
)
;
sortSpecification
: #(
SORT_SPEC sortKeySpecification (collationSpecification)? (orderingSpecification)?
)
;
sortKeySpecification
: #(SORT_KEY sortKey)
;
sortKey
: i:IDENT {
out( #i );
}
;
collationSpecification
: c:COLLATE {
out( " collate " );
out( c );
}
;
orderingSpecification
: o:ORDER_SPEC {
out( " " );
out( #o );
}
;

View File

@ -0,0 +1,440 @@
header
{
/*
* 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.sql.ordering.antlr;
}
/**
* Antlr grammar for dealing with <tt>order-by</tt> mapping fragments.
* @author Steve Ebersole
*/
class GeneratedOrderByFragmentParser extends Parser;
options
{
exportVocab=OrderByTemplate;
buildAST=true;
k=3;
}
tokens
{
// synthetic tokens
ORDER_BY;
SORT_SPEC;
ORDER_SPEC;
SORT_KEY;
EXPR_LIST;
DOT;
IDENT_LIST;
COLUMN_REF;
COLLATE="collate";
ASCENDING="asc";
DESCENDING="desc";
}
{
/**
* Method for logging execution trace information.
*
* @param msg The trace message.
*/
protected void trace(String msg) {
System.out.println( msg );
}
/**
* Extract a node's text.
*
* @param ast The node
*
* @return The text.
*/
protected final String extractText(AST ast) {
// for some reason, within AST creation blocks "[]" I am somtimes unable to refer to the AST.getText() method
// using #var (the #var is not interpreted as the rule's output AST).
return ast.getText();
}
/**
* Process the given node as a quote identifier. These need to be quoted in the dialect-specific way.
*
* @param ident The quoted-identifier node.
*
* @return The processed node.
*
* @see org.hibernate.dialect.Dialect#quote
*/
protected AST quotedIdentifier(AST ident) {
return ident;
}
/**
* Process the given node as a quote string.
*
* @param ident The quoted string. This is used from within function param recognition, and represents a
* SQL-quoted string.
*
* @return The processed node.
*/
protected AST quotedString(AST ident) {
return ident;
}
/**
* A check to see if the text of the given node represents a known function name.
*
* @param ast The node whose text we want to check.
*
* @return True if the node's text is a known function name, false otherwise.
*
* @see org.hibernate.dialect.function.SQLFunctionRegistry
*/
protected boolean isFunctionName(AST ast) {
return false;
}
/**
* Process the given node as a function.
*
* @param The node representing the function invocation (including parameters as subtree components).
*
* @return The processed node.
*/
protected AST resolveFunction(AST ast) {
return ast;
}
/**
* Process the given node as an IDENT. May represent either a column reference or a property reference.
*
* @param ident The node whose text represents either a column or property reference.
*
* @return The processed node.
*/
protected AST resolveIdent(AST ident) {
return ident;
}
/**
* Allow post processing of each <tt>sort specification</tt>
*
* @param The grammar-built sort specification subtree.
*
* @return The processed sort specification subtree.
*/
protected AST postProcessSortSpecification(AST sortSpec) {
return sortSpec;
}
}
/**
* Main recognition rule for this grammar
*/
orderByFragment { trace("orderByFragment"); }
: sortSpecification ( COMMA! sortSpecification )* {
#orderByFragment = #( [ORDER_BY, "order-by"], #orderByFragment );
}
;
/**
* Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which
* the results should be sorted.
*/
sortSpecification { trace("sortSpecification"); }
: sortKey (collationSpecification)? (orderingSpecification)? {
#sortSpecification = #( [SORT_SPEC, "{sort specification}"], #sortSpecification );
#sortSpecification = postProcessSortSpecification( #sortSpecification );
}
;
/**
* Reconition rule for what ANSI SQL terms the <tt>sort key</tt> which is the expression (column, function, etc) upon
* which to base the sorting.
*/
sortKey! { trace("sortKey"); }
: e:expression {
#sortKey = #( [SORT_KEY, "sort key"], #e );
}
;
/**
* Reconition rule what this grammar recognizes as valid <tt>sort key</tt>.
*/
expression! { trace("expression"); }
: HARD_QUOTE qi:IDENT HARD_QUOTE {
#expression = quotedIdentifier( #qi );
}
| ( IDENT (DOT IDENT)* OPEN_PAREN ) => f:functionCall {
#expression = #f;
}
| p:simplePropertyPath {
#expression = resolveIdent( #p );
}
| i:IDENT {
if ( isFunctionName( #i ) ) {
#expression = resolveFunction( #i );
}
else {
#expression = resolveIdent( #i );
}
}
;
/**
* Intended for use as a syntactic predicate to determine whether an IDENT represents a known SQL function name.
*/
functionCallCheck! { trace("functionCallCheck"); }
: IDENT (DOT IDENT)* OPEN_PAREN { true }?
;
/**
* Recognition rule for a function call
*/
functionCall! { trace("functionCall"); }
: fn:functionName OPEN_PAREN pl:functionParameterList CLOSE_PAREN {
#functionCall = #( [IDENT, extractText( #fn )], #pl );
#functionCall = resolveFunction( #functionCall );
}
;
/**
* A function-name is an IDENT followed by zero or more (DOT IDENT) sequences
*/
functionName {
trace("functionName");
StringBuffer buffer = new StringBuffer();
}
: i:IDENT { buffer.append( i.getText() ); }
( DOT i2:IDENT { buffer.append( '.').append( i2.getText() ); } )* {
#functionName = #( [IDENT,buffer.toString()] );
}
;
/**
* Recognition rule used to "wrap" all function parameters into an EXPR_LIST node
*/
functionParameterList { trace("functionParameterList"); }
: functionParameter ( COMMA! functionParameter )* {
#functionParameterList = #( [EXPR_LIST, "{param list}"], #functionParameterList );
}
;
/**
* Recognized function parameters.
*/
functionParameter { trace("functionParameter"); }
: expression
| NUM_DOUBLE
| NUM_FLOAT
| NUM_INT
| NUM_LONG
| QUOTED_STRING {
#functionParameter = quotedString( #functionParameter );
}
;
/**
* Reconition rule for what ANSI SQL terms the <tt>collation specification</tt> used to allow specifying that sorting for
* the given {@link #sortSpecification} be treated within a specific character-set.
*/
collationSpecification! { trace("collationSpecification"); }
: c:COLLATE cn:collationName {
#collationSpecification = #( [COLLATE, extractText( #cn )] );
}
;
/**
* The collation name wrt {@link #collationSpecification}. Namely, the character-set.
*/
collationName { trace("collationSpecification"); }
: IDENT
;
/**
* Reconition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or
* <tt>DESCENDING</tt>.
*/
orderingSpecification! { trace("orderingSpecification"); }
: ( "asc" | "ascending" ) {
#orderingSpecification = #( [ORDER_SPEC, "asc"] );
}
| ( "desc" | "descending") {
#orderingSpecification = #( [ORDER_SPEC, "desc"] );
}
;
/**
* A simple-property-path is an IDENT followed by one or more (DOT IDENT) sequences
*/
simplePropertyPath {
trace("simplePropertyPath");
StringBuffer buffer = new StringBuffer();
}
: i:IDENT { buffer.append( i.getText() ); }
( DOT i2:IDENT { buffer.append( '.').append( i2.getText() ); } )+ {
#simplePropertyPath = #( [IDENT,buffer.toString()] );
}
;
// **** LEXER ******************************************************************
/**
* Lexer for the <tt>order-by</tt> fragment parser
* @author Steve Ebersole
* @author Joshua Davis
*/
class GeneratedOrderByLexer extends Lexer;
options {
exportVocab=OrderByTemplate;
testLiterals = false;
k=2;
charVocabulary='\u0000'..'\uFFFE'; // Allow any char but \uFFFF (16 bit -1, ANTLR's EOF character)
caseSensitive = false;
caseSensitiveLiterals = false;
}
// -- Keywords --
OPEN_PAREN: '(';
CLOSE_PAREN: ')';
COMMA: ',';
HARD_QUOTE: '`';
IDENT options { testLiterals=true; }
: ID_START_LETTER ( ID_LETTER )*
;
protected
ID_START_LETTER
: '_'
| '$'
| 'a'..'z'
| '\u0080'..'\ufffe' // HHH-558 : Allow unicode chars in identifiers
;
protected
ID_LETTER
: ID_START_LETTER
| '0'..'9'
;
QUOTED_STRING
: '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\''
;
protected
ESCqs
:
'\'' '\''
;
//--- From the Java example grammar ---
// a numeric literal
NUM_INT
{boolean isDecimal=false; Token t=null;}
: '.' {_ttype = DOT;}
( ('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
{
if (t != null && t.getText().toUpperCase().indexOf('F')>=0)
{
_ttype = NUM_FLOAT;
}
else
{
_ttype = NUM_DOUBLE; // assume double
}
}
)?
| ( '0' {isDecimal = true;} // special case for just '0'
( ('x')
( // hex
// the 'e'|'E' and float suffix stuff look
// like hex digits, hence the (...)+ doesn't
// know when to stop: ambig. ANTLR resolves
// it correctly by matching immediately. It
// is therefore ok to hush warning.
options { warnWhenFollowAmbig=false; }
: HEX_DIGIT
)+
| ('0'..'7')+ // octal
)?
| ('1'..'9') ('0'..'9')* {isDecimal=true;} // non-zero decimal
)
( ('l') { _ttype = NUM_LONG; }
// only check to see if it's a float if looks like decimal so far
| {isDecimal}?
( '.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})?
| EXPONENT (f3:FLOAT_SUFFIX {t=f3;})?
| f4:FLOAT_SUFFIX {t=f4;}
)
{
if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0)
{
_ttype = NUM_FLOAT;
}
else
{
_ttype = NUM_DOUBLE; // assume double
}
}
)?
;
// hexadecimal digit (again, note it's protected!)
protected
HEX_DIGIT
: ('0'..'9'|'a'..'f')
;
// a couple protected methods to assist in matching floating point numbers
protected
EXPONENT
: ('e') ('+'|'-')? ('0'..'9')+
;
protected
FLOAT_SUFFIX
: 'f'|'d'
;
WS : ( ' '
| '\t'
| '\r' '\n' { newline(); }
| '\n' { newline(); }
| '\r' { newline(); }
)
{$setType(Token.SKIP);} //ignore this token
;

View File

@ -80,12 +80,12 @@ import org.hibernate.sql.Alias;
import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SelectFragment;
import org.hibernate.sql.SimpleSelect; import org.hibernate.sql.SimpleSelect;
import org.hibernate.sql.Template; import org.hibernate.sql.Template;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.type.AbstractComponentType; import org.hibernate.type.AbstractComponentType;
import org.hibernate.type.CollectionType; import org.hibernate.type.CollectionType;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.util.ArrayHelper; import org.hibernate.util.ArrayHelper;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.FilterHelper; import org.hibernate.util.FilterHelper;
import org.hibernate.util.StringHelper; import org.hibernate.util.StringHelper;
@ -113,7 +113,6 @@ public abstract class AbstractCollectionPersister
private final String sqlDetectRowByIndexString; private final String sqlDetectRowByIndexString;
private final String sqlDetectRowByElementString; private final String sqlDetectRowByElementString;
private final String sqlOrderByString;
protected final String sqlWhereString; protected final String sqlWhereString;
private final String sqlOrderByStringTemplate; private final String sqlOrderByStringTemplate;
private final String sqlWhereStringTemplate; private final String sqlWhereStringTemplate;
@ -197,7 +196,7 @@ public abstract class AbstractCollectionPersister
private final String manyToManyWhereString; private final String manyToManyWhereString;
private final String manyToManyWhereTemplate; private final String manyToManyWhereTemplate;
private final String manyToManyOrderByString; private final boolean hasManyToManyOrder;
private final String manyToManyOrderByTemplate; private final String manyToManyOrderByTemplate;
// custom sql // custom sql
@ -267,11 +266,6 @@ public abstract class AbstractCollectionPersister
spaces[i] = (String) iter.next(); spaces[i] = (String) iter.next();
} }
sqlOrderByString = collection.getOrderBy();
hasOrder = sqlOrderByString != null;
sqlOrderByStringTemplate = hasOrder ?
Template.renderOrderByStringTemplate(sqlOrderByString, dialect, factory.getSqlFunctionRegistry()) :
null;
sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null; sqlWhereString = StringHelper.isNotEmpty( collection.getWhere() ) ? "( " + collection.getWhere() + ") " : null;
hasWhere = sqlWhereString != null; hasWhere = sqlWhereString != null;
sqlWhereStringTemplate = hasWhere ? sqlWhereStringTemplate = hasWhere ?
@ -541,6 +535,25 @@ public abstract class AbstractCollectionPersister
} }
} }
hasOrder = collection.getOrderBy() != null;
if ( hasOrder ) {
ColumnMapper mapper = new ColumnMapper() {
public String[] map(String reference) {
return elementPropertyMapping.toColumns( reference );
}
};
sqlOrderByStringTemplate = Template.renderOrderByStringTemplate(
collection.getOrderBy(),
mapper,
factory,
dialect,
factory.getSqlFunctionRegistry()
);
}
else {
sqlOrderByStringTemplate = null;
}
// Handle any filters applied to this collection // Handle any filters applied to this collection
filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() ); filterHelper = new FilterHelper( collection.getFilterMap(), dialect, factory.getSqlFunctionRegistry() );
@ -552,10 +565,25 @@ public abstract class AbstractCollectionPersister
manyToManyWhereTemplate = manyToManyWhereString == null ? manyToManyWhereTemplate = manyToManyWhereString == null ?
null : null :
Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() ); Template.renderWhereStringTemplate( manyToManyWhereString, factory.getDialect(), factory.getSqlFunctionRegistry() );
manyToManyOrderByString = collection.getManyToManyOrdering();
manyToManyOrderByTemplate = manyToManyOrderByString == null hasManyToManyOrder = collection.getManyToManyOrdering() != null;
? null if ( hasManyToManyOrder ) {
: Template.renderOrderByStringTemplate( manyToManyOrderByString, factory.getDialect(), factory.getSqlFunctionRegistry() ); ColumnMapper mapper = new ColumnMapper() {
public String[] map(String reference) {
return elementPropertyMapping.toColumns( reference );
}
};
manyToManyOrderByTemplate = Template.renderOrderByStringTemplate(
collection.getManyToManyOrdering(),
mapper,
factory,
dialect,
factory.getSqlFunctionRegistry()
);
}
else {
manyToManyOrderByTemplate = null;
}
initCollectionPropertyMap(); initCollectionPropertyMap();
} }
@ -658,17 +686,15 @@ public abstract class AbstractCollectionPersister
} }
public String getSQLOrderByString(String alias) { public String getSQLOrderByString(String alias) {
return hasOrdering() ? return hasOrdering()
StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias ) : ""; ? StringHelper.replace( sqlOrderByStringTemplate, Template.TEMPLATE, alias )
: "";
} }
public String getManyToManyOrderByString(String alias) { public String getManyToManyOrderByString(String alias) {
if ( isManyToMany() && manyToManyOrderByString != null ) { return hasManyToManyOrdering()
return StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias ); ? StringHelper.replace( manyToManyOrderByTemplate, Template.TEMPLATE, alias )
} : "";
else {
return "";
}
} }
public FetchMode getFetchMode() { public FetchMode getFetchMode() {
return fetchMode; return fetchMode;
@ -679,7 +705,7 @@ public abstract class AbstractCollectionPersister
} }
public boolean hasManyToManyOrdering() { public boolean hasManyToManyOrdering() {
return isManyToMany() && manyToManyOrderByTemplate != null; return isManyToMany() && hasManyToManyOrder;
} }
public boolean hasWhere() { public boolean hasWhere() {

View File

@ -30,6 +30,10 @@ import java.util.StringTokenizer;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry; import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.util.StringHelper; import org.hibernate.util.StringHelper;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.sql.ordering.antlr.TranslationContext;
import org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator;
import org.hibernate.engine.SessionFactoryImplementor;
/** /**
* Parses SQL fragments specified in mapping documents * Parses SQL fragments specified in mapping documents
@ -233,91 +237,77 @@ public final class Template {
return result.toString(); return result.toString();
} }
/** public static class NoOpColumnMapper implements ColumnMapper {
* Takes order by clause provided in the mapping attribute and interpolates the alias. public static final NoOpColumnMapper INSTANCE = new NoOpColumnMapper();
* Handles asc, desc, SQL functions, quoted identifiers. public String[] map(String reference) {
*/ return new String[] { reference };
public static String renderOrderByStringTemplate(String sqlOrderByString, Dialect dialect, SQLFunctionRegistry functionRegistry) {
//TODO: make this a bit nicer
String symbols = new StringBuffer()
.append("=><!+-*/()',|&`")
.append(StringHelper.WHITESPACE)
.append( dialect.openQuote() )
.append( dialect.closeQuote() )
.toString();
StringTokenizer tokens = new StringTokenizer(sqlOrderByString, symbols, true);
StringBuffer result = new StringBuffer();
boolean quoted = false;
boolean quotedIdentifier = false;
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;
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;
isQuoteCharacter = true;
}
else if ( !quotedIdentifier && ( dialect.openQuote()==token.charAt(0) ) ) {
isOpenQuote = true;
quotedIdentifier = true;
isQuoteCharacter = true;
}
else if ( quotedIdentifier && ( dialect.closeQuote()==token.charAt(0) ) ) {
quotedIdentifier = false;
isQuoteCharacter = true;
isOpenQuote = false;
}
else {
isOpenQuote = false;
}
if (isOpenQuote) {
result.append(TEMPLATE).append('.');
}
}
boolean quotedOrWhitespace = quoted ||
quotedIdentifier ||
isQuoteCharacter ||
Character.isWhitespace( token.charAt(0) );
if (quotedOrWhitespace) {
result.append(token);
}
else if (
isIdentifier(token, dialect) &&
!isFunctionOrKeyword(lcToken, nextToken, dialect, functionRegistry)
) {
result.append(TEMPLATE)
.append('.')
.append( dialect.quote(token) );
}
else {
result.append(token);
}
} }
return result.toString(); }
/**
* Performs order-by template rendering without {@link ColumnMapper column mapping}. An <tt>ORDER BY</tt> template
* has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE}
*
* @param orderByFragment The order-by fragment to render.
* @param dialect The SQL dialect being used.
* @param functionRegistry The SQL function registry
*
* @return The rendered <tt>ORDER BY</tt> template.
*
* @see #renderOrderByStringTemplate(String,ColumnMapper,SessionFactoryImplementor,Dialect,SQLFunctionRegistry)
*/
public static String renderOrderByStringTemplate(
String orderByFragment,
Dialect dialect,
SQLFunctionRegistry functionRegistry) {
return renderOrderByStringTemplate(
orderByFragment,
NoOpColumnMapper.INSTANCE,
null,
dialect,
functionRegistry
);
}
/**
* Performs order-by template rendering allowing {@link ColumnMapper column mapping}. An <tt>ORDER BY</tt> template
* has all column references "qualified" with a placeholder identified by {@link Template#TEMPLATE} which can later
* be used to easily inject the SQL alias.
*
* @param orderByFragment The order-by fragment to render.
* @param columnMapper The column mapping strategy to use.
* @param sessionFactory The session factory.
* @param dialect The SQL dialect being used.
* @param functionRegistry The SQL function registry
*
* @return The rendered <tt>ORDER BY</tt> template.
*/
public static String renderOrderByStringTemplate(
String orderByFragment,
final ColumnMapper columnMapper,
final SessionFactoryImplementor sessionFactory,
final Dialect dialect,
final SQLFunctionRegistry functionRegistry) {
TranslationContext context = new TranslationContext() {
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
public Dialect getDialect() {
return dialect;
}
public SQLFunctionRegistry getSqlFunctionRegistry() {
return functionRegistry;
}
public ColumnMapper getColumnMapper() {
return columnMapper;
}
};
OrderByFragmentTranslator translator = new OrderByFragmentTranslator( context );
return translator.render( orderByFragment );
} }
private static boolean isNamedParameter(String token) { private static boolean isNamedParameter(String token) {

View File

@ -0,0 +1,34 @@
/*
* 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.sql.ordering.antlr;
/**
* Models a collation specification (<tt>COLLATE</tt> using a specific character-set) within a
* {@link SortSpecification}.
*
* @author Steve Ebersole
*/
public class CollationSpecification extends NodeSupport {
}

View File

@ -0,0 +1,46 @@
/*
* 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.sql.ordering.antlr;
import org.hibernate.HibernateException;
/**
* Contract for mapping a (an assumed) property reference to its columns.
*
* @author Steve Ebersole
*/
public interface ColumnMapper {
/**
* Resolve the property reference to its underlying columns.
*
* @param reference The property reference name.
*
* @return The underlying columns, or null if the property reference is unknown.
*
* @throws HibernateException Generally indicates that the property reference is unkown; interpretation is the
* same as a null return.
*/
public String[] map(String reference) throws HibernateException;
}

View File

@ -0,0 +1,54 @@
/*
* 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.sql.ordering.antlr;
import antlr.ASTFactory;
/**
* Acts as a {@link ASTFactory} for injecting our specific AST node classes into the Antlr generated trees.
*
* @author Steve Ebersole
*/
public class Factory extends ASTFactory implements OrderByTemplateTokenTypes {
/**
* {@inheritDoc}
*/
public Class getASTNodeType(int i) {
switch ( i ) {
case ORDER_BY:
return OrderByFragment.class;
case SORT_SPEC:
return SortSpecification.class;
case ORDER_SPEC:
return OrderingSpecification.class;
case COLLATE:
return CollationSpecification.class;
case SORT_KEY:
return SortKey.class;
default:
return NodeSupport.class;
}
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.sql.ordering.antlr;
/**
* General contract for AST nodes.
*
* @author Steve Ebersole
*/
public interface Node {
/**
* Get the intrinsic text of this node.
*
* @return The node's text.
*/
public String getText();
/**
* Get a string representation of this node usable for debug logging or similar.
*
* @return The node's debugging text.
*/
public String getDebugText();
/**
* Build the node's representation for use in the resulting rendering.
*
* @return The renderable text.
*/
public String getRenderableText();
}

View File

@ -0,0 +1,48 @@
/*
* 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.sql.ordering.antlr;
import antlr.CommonAST;
/**
* Basic implementation of a {@link Node}.
*
* @author Steve Ebersole
*/
public class NodeSupport extends CommonAST implements Node {
/**
* {@inheritDoc}
*/
public String getDebugText() {
return getText();
}
/**
* {@inheritDoc}
*/
public String getRenderableText() {
return getText();
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.sql.ordering.antlr;
/**
* Represents a parsed <tt>order-by</tt> mapping fragment. This holds the tree of all {@link SortSpecification}s.
*
* @author Steve Ebersole
*/
public class OrderByFragment extends NodeSupport {
}

View File

@ -0,0 +1,194 @@
/*
* 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.sql.ordering.antlr;
import java.util.ArrayList;
import antlr.TokenStream;
import antlr.CommonAST;
import antlr.collections.AST;
import org.hibernate.sql.Template;
import org.hibernate.dialect.function.SQLFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior.
*
* @author Steve Ebersole
*/
public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
private static final Logger log = LoggerFactory.getLogger( OrderByFragmentParser.class );
private final TranslationContext context;
public OrderByFragmentParser(TokenStream lexer, TranslationContext context) {
super( lexer );
super.setASTFactory( new Factory() );
this.context = context;
}
/**
* {@inheritDoc}
*/
protected void trace(String msg) {
log.trace( msg );
}
/**
* {@inheritDoc}
*/
protected AST quotedIdentifier(AST ident) {
return getASTFactory().create(
OrderByTemplateTokenTypes.IDENT,
Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' )
);
}
/**
* {@inheritDoc}
*/
protected AST quotedString(AST ident) {
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, context.getDialect().quote( ident.getText() ) );
}
/**
* {@inheritDoc}
*/
protected boolean isFunctionName(AST ast) {
return context.getSqlFunctionRegistry().hasFunction( ast.getText() );
}
/**
* {@inheritDoc}
*/
protected AST resolveFunction(AST ast) {
AST child = ast.getFirstChild();
assert "{param list}".equals( child.getText() );
child = child.getFirstChild();
final String functionName = ast.getText();
final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( functionName );
if ( function == null ) {
String text = functionName;
if ( child != null ) {
text += '(';
while ( child != null ) {
text += child.getText();
child = child.getNextSibling();
if ( child != null ) {
text += ", ";
}
}
text += ')';
}
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text );
}
else {
ArrayList expressions = new ArrayList();
while ( child != null ) {
expressions.add( child.getText() );
child = child.getNextSibling();
}
final String text = function.render( expressions, context.getSessionFactory() );
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, text );
}
}
/**
* {@inheritDoc}
*/
protected AST resolveIdent(AST ident) {
String text = ident.getText();
String[] replacements;
try {
replacements = context.getColumnMapper().map( text );
}
catch( Throwable t ) {
replacements = null;
}
if ( replacements == null || replacements.length == 0 ) {
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + ident );
}
else if ( replacements.length == 1 ) {
return getASTFactory().create( OrderByTemplateTokenTypes.IDENT, Template.TEMPLATE + "." + replacements[0] );
}
else {
final AST root = getASTFactory().create( OrderByTemplateTokenTypes.IDENT_LIST, "{ident list}" );
for ( int i = 0; i < replacements.length; i++ ) {
final String identText = Template.TEMPLATE + '.' + replacements[i];
root.addChild( getASTFactory().create( OrderByTemplateTokenTypes.IDENT, identText ) );
}
return root;
}
}
/**
* {@inheritDoc}
*/
protected AST postProcessSortSpecification(AST sortSpec) {
assert SORT_SPEC == sortSpec.getType();
SortSpecification sortSpecification = ( SortSpecification ) sortSpec;
AST sortKey = sortSpecification.getSortKey();
if ( IDENT_LIST == sortKey.getFirstChild().getType() ) {
AST identList = sortKey.getFirstChild();
AST ident = identList.getFirstChild();
AST holder = new CommonAST();
do {
holder.addChild(
createSortSpecification(
ident,
sortSpecification.getCollation(),
sortSpecification.getOrdering()
)
);
ident = ident.getNextSibling();
} while ( ident != null );
sortSpec = holder.getFirstChild();
}
return sortSpec;
}
private SortSpecification createSortSpecification(
AST ident,
CollationSpecification collationSpecification,
OrderingSpecification orderingSpecification) {
AST sortSpecification = getASTFactory().create( SORT_SPEC, "{{sort specification}}" );
AST sortKey = getASTFactory().create( SORT_KEY, "{{sort key}}" );
AST newIdent = getASTFactory().create( ident.getType(), ident.getText() );
sortKey.setFirstChild( newIdent );
sortSpecification.setFirstChild( sortKey );
if ( collationSpecification != null ) {
sortSpecification.addChild( collationSpecification );
}
if ( orderingSpecification != null ) {
sortSpecification.addChild( orderingSpecification );
}
return ( SortSpecification ) sortSpecification;
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.sql.ordering.antlr;
import antlr.collections.AST;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class OrderByFragmentRenderer extends GeneratedOrderByFragmentRenderer {
protected void out(AST ast) {
out( ( ( Node ) ast ).getRenderableText() );
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.sql.ordering.antlr;
import java.io.StringReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.hql.ast.util.ASTPrinter;
/**
* A translator which coordinates translation of an <tt>order-by</tt> mapping.
*
* @author Steve Ebersole
*/
public class OrderByFragmentTranslator {
private static final Logger log = LoggerFactory.getLogger( OrderByFragmentTranslator.class );
public final TranslationContext context;
public OrderByFragmentTranslator(TranslationContext context) {
this.context = context;
}
/**
* The main contract, performing the transaction.
*
* @param fragment The <tt>order-by</tt> mapping fragment to be translated.
*
* @return The translated fragment.
*/
public String render(String fragment) {
GeneratedOrderByLexer lexer = new GeneratedOrderByLexer( new StringReader( fragment ) );
OrderByFragmentParser parser = new OrderByFragmentParser( lexer, context );
try {
parser.orderByFragment();
}
catch ( HibernateException e ) {
throw e;
}
catch ( Throwable t ) {
throw new HibernateException( "Unable to parse order-by fragment", t );
}
if ( log.isTraceEnabled() ) {
ASTPrinter printer = new ASTPrinter( OrderByTemplateTokenTypes.class );
log.trace( printer.showAsString( parser.getAST(), "--- {order-by fragment} ---" ) );
}
GeneratedOrderByFragmentRenderer renderer = new GeneratedOrderByFragmentRenderer();
try {
renderer.orderByFragment( parser.getAST() );
}
catch ( HibernateException e ) {
throw e;
}
catch ( Throwable t ) {
throw new HibernateException( "Unable to render parsed order-by fragment", t );
}
return renderer.getRenderedFragment();
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.sql.ordering.antlr;
/**
* Models an ordering specification (<tt>ASCENDING</tt> or <tt>DESCENDING</tt>) within a {@link SortSpecification}.
*
* @author Steve Ebersole
*/
public class OrderingSpecification extends NodeSupport {
public static class Ordering {
public static final Ordering ASCENDING = new Ordering( "asc" );
public static final Ordering DESCENDING = new Ordering( "desc" );
private final String name;
private Ordering(String name) {
this.name = name;
}
}
private boolean resolved;
private Ordering ordering;
public Ordering getOrdering() {
if ( !resolved ) {
ordering = resolve( getText() );
resolved = true;
}
return ordering;
}
private static Ordering resolve(String text) {
if ( Ordering.ASCENDING.name.equals( text ) ) {
return Ordering.ASCENDING;
}
else if ( Ordering.DESCENDING.name.equals( text ) ) {
return Ordering.DESCENDING;
}
else {
throw new IllegalStateException( "Unknown ordering [" + text + "]" );
}
}
public String getRenderableText() {
return getOrdering().name;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.sql.ordering.antlr;
/**
* Models the container node for the <tt>sort key</tt>, which is the term given by the ANSI SQL specification to the
* expression upon which to sort for each {@link SortSpecification}
*
* @author Steve Ebersole
*/
public class SortKey extends NodeSupport {
}

View File

@ -0,0 +1,80 @@
/*
* 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.sql.ordering.antlr;
import antlr.collections.AST;
/**
* Models each sorting exprersion.
*
* @author Steve Ebersole
*/
public class SortSpecification extends NodeSupport {
/**
* Locate the specified {@link SortKey}.
*
* @return The sort key.
*/
public SortKey getSortKey() {
return ( SortKey ) getFirstChild();
}
/**
* Locate the specified <tt>collation specification</tt>, if one.
*
* @return The <tt>collation specification</tt>, or null if none was specified.
*/
public CollationSpecification getCollation() {
AST possible = getSortKey().getNextSibling();
return possible != null && OrderByTemplateTokenTypes.COLLATE == possible.getType()
? ( CollationSpecification ) possible
: null;
}
/**
* Locate the specified <tt>ordering specification</tt>, if one.
*
* @return The <tt>ordering specification</tt>, or null if none was specified.
*/
public OrderingSpecification getOrdering() {
// IMPL NOTE : the ordering-spec would be either the 2nd or 3rd child (of the overall sort-spec), if it existed,
// depending on whether a collation-spec was specified.
AST possible = getSortKey().getNextSibling();
if ( possible == null ) {
// There was no sort-spec parts specified other then the sort-key so there can be no ordering-spec...
return null;
}
if ( OrderByTemplateTokenTypes.COLLATE == possible.getType() ) {
// the 2nd child was a collation-spec, so we need to check the 3rd child instead.
possible = possible.getNextSibling();
}
return possible != null && OrderByTemplateTokenTypes.ORDER_SPEC == possible.getType()
? ( OrderingSpecification ) possible
: null;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.sql.ordering.antlr;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry;
/**
* Contract for contextual information required to perform translation.
*
* @author Steve Ebersole
*/
public interface TranslationContext {
/**
* Retrieves the <tt>session factory</tt> for this context.
*
* @return The <tt>session factory</tt>
*/
public SessionFactoryImplementor getSessionFactory();
/**
* Retrieves the <tt>dialect</tt> for this context.
*
* @return The <tt>dialect</tt>
*/
public Dialect getDialect();
/**
* Retrieves the <tt>SQL function registry/tt> for this context.
*
* @return The SQL function registry.
*/
public SQLFunctionRegistry getSqlFunctionRegistry();
/**
* Retrieves the <tt>column mapper</tt> for this context.
*
* @return The <tt>column mapper</tt>
*/
public ColumnMapper getColumnMapper();
}

View File

@ -0,0 +1,184 @@
/*
* 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.sql;
import java.util.Collections;
import junit.framework.TestCase;
import org.hibernate.persister.entity.PropertyMapping;
import org.hibernate.type.Type;
import org.hibernate.QueryException;
import org.hibernate.sql.ordering.antlr.ColumnMapper;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.dialect.function.SQLFunctionRegistry;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class TemplateTest extends TestCase {
private static final PropertyMapping PROPERTY_MAPPING = new PropertyMapping() {
public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
if ( "sql".equals( propertyName ) ) {
return new String[] { "sql" };
}
else if ( "component".equals( propertyName ) ) {
return new String[] { "comp_1", "comp_2" };
}
else if ( "component.prop1".equals( propertyName ) ) {
return new String[] { "comp_1" };
}
else if ( "component.prop2".equals( propertyName ) ) {
return new String[] { "comp_2" };
}
else if ( "property".equals( propertyName ) ) {
return new String[] { "prop" };
}
throw new QueryException( "could not resolve property: " + propertyName );
}
public Type toType(String propertyName) throws QueryException {
return null;
}
public String[] toColumns(String alias, String propertyName) throws QueryException {
return new String[0];
}
public Type getType() {
return null;
}
};
private static final ColumnMapper MAPPER = new ColumnMapper() {
public String[] map(String reference) {
return PROPERTY_MAPPING.toColumns( reference );
}
};
private static final Dialect DIALECT = new HSQLDialect();
private static final SQLFunctionRegistry FUNCTION_REGISTRY = new SQLFunctionRegistry( DIALECT, Collections.EMPTY_MAP );
public void testSQLReferences() {
String fragment = "sql asc, sql desc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".sql asc, " + Template.TEMPLATE + ".sql desc", template );
}
public void testQuotedSQLReferences() {
String fragment = "`sql` asc, `sql` desc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".\"sql\" asc, " + Template.TEMPLATE + ".\"sql\" desc", template );
}
public void testPropertyReference() {
String fragment = "property asc, property desc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".prop asc, " + Template.TEMPLATE + ".prop desc", template );
}
public void testFunctionReference() {
String fragment = "upper(sql) asc, lower(sql) desc";
String template = doStandardRendering( fragment );
assertEquals( "upper(" + Template.TEMPLATE + ".sql) asc, lower(" + Template.TEMPLATE + ".sql) desc", template );
}
public void testQualifiedFunctionReference() {
String fragment = "qual.upper(property) asc, qual.lower(property) desc";
String template = doStandardRendering( fragment );
assertEquals( "qual.upper(" + Template.TEMPLATE + ".prop) asc, qual.lower(" + Template.TEMPLATE + ".prop) desc", template );
}
public void testDoubleQualifiedFunctionReference() {
String fragment = "qual1.qual2.upper(property) asc, qual1.qual2.lower(property) desc";
String template = doStandardRendering( fragment );
assertEquals( "qual1.qual2.upper(" + Template.TEMPLATE + ".prop) asc, qual1.qual2.lower(" + Template.TEMPLATE + ".prop) desc", template );
}
public void testFunctionWithPropertyReferenceAsParam() {
String fragment = "upper(property) asc, lower(property) desc";
String template = doStandardRendering( fragment );
assertEquals( "upper(" + Template.TEMPLATE + ".prop) asc, lower(" + Template.TEMPLATE + ".prop) desc", template );
}
public void testNestedFunctionReferences() {
String fragment = "upper(lower(sql)) asc, lower(upper(sql)) desc";
String template = doStandardRendering( fragment );
assertEquals( "upper(lower(" + Template.TEMPLATE + ".sql)) asc, lower(upper(" + Template.TEMPLATE + ".sql)) desc", template );
}
public void testComplexNestedFunctionReferences() {
String fragment = "mod(mod(sql,2),3) asc";
String template = doStandardRendering( fragment );
assertEquals( "mod(mod(" + Template.TEMPLATE + ".sql, 2), 3) asc", template );
}
public void testCollation() {
String fragment = "`sql` COLLATE my_collation, `sql` COLLATE your_collation";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".\"sql\" collate my_collation, " + Template.TEMPLATE + ".\"sql\" collate your_collation", template );
}
public void testCollationAndOrdering() {
String fragment = "sql COLLATE my_collation, upper(prop) COLLATE your_collation asc, `sql` desc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".sql collate my_collation, upper(" + Template.TEMPLATE + ".prop) collate your_collation asc, " + Template.TEMPLATE + ".\"sql\" desc", template );
}
public void testComponentReferences() {
String fragment = "component asc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".comp_1 asc, " + Template.TEMPLATE + ".comp_2 asc", template );
}
public void testComponentDerefReferences() {
String fragment = "component.prop1 asc";
String template = doStandardRendering( fragment );
assertEquals( Template.TEMPLATE + ".comp_1 asc", template );
}
public String doStandardRendering(String fragment) {
return Template.renderOrderByStringTemplate( fragment, MAPPER, null, DIALECT, FUNCTION_REGISTRY );
}
}

View File

@ -21,7 +21,6 @@
# 51 Franklin Street, Fifth Floor # 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301 USA # Boston, MA 02110-1301 USA
# #
#
log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
@ -31,3 +30,4 @@ log4j.rootLogger=info, stdout
log4j.logger.org.hibernate.test=info log4j.logger.org.hibernate.test=info
log4j.logger.org.hibernate.tool.hbm2ddl=debug log4j.logger.org.hibernate.tool.hbm2ddl=debug
log4j.logger.org.hibernate.sql.ordering.antlr.OrderByFragmentTranslator=trace