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:
parent
13ddb15f29
commit
538982e8e6
|
@ -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>
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
;
|
|
@ -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
|
||||||
|
;
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue