[hibernate-commits] Hibernate SVN: r16437 - in core/branches/antlr3/src: main/antlr3/org/hibernate/sql/ast/phase/hql/parse and 4 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Fri Apr 24 17:05:09 EDT 2009
Author: steve.ebersole at jboss.com
Date: 2009-04-24 17:05:09 -0400 (Fri, 24 Apr 2009)
New Revision: 16437
Added:
core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/
core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java
Modified:
core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g
core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g
core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java
core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java
core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java
core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java
Log:
first phase of order-by mapping fragment processing using the new unified sql rendering
Modified: core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g
===================================================================
--- core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/ordering/OrderByParser.g 2009-04-24 21:05:09 UTC (rev 16437)
@@ -1,3 +1,12 @@
+parser grammar OrderByParser;
+
+options {
+ tokenVocab=HQLLexer;
+ output=AST;
+ ASTLabelType=CommonTree;
+}
+
+ at parser::header {
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
@@ -21,148 +30,65 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
+ * Portions of SQL grammar parsing copyright (C) 2003 by Lubos Vnuk. All rights
+ * reserved. These portions are distributed under license by Red Hat Middleware
+ * LLC and are covered by the above LGPL notice. If you redistribute this material,
+ * with or without modification, you must preserve this copyright notice in its
+ * entirety.
*/
-grammar OrderByParser;
+package org.hibernate.sql.ast.ordering;
-options {
- language = Java;
- output = AST;
- ASTLabelType = CommonTree;
+import org.hibernate.sql.Template;
}
-tokens {
- COLLATE;
- ASCENDING;
- DESCENDING;
-
- ORDER_BY;
- SORT_SPEC;
- ORDER_SPEC;
- SORT_KEY;
- EXPR_LIST;
- IDENT_LIST;
- COLUMN_REF;
-
- NUM_INTEGER_LITERAL;
- NUM_LONG_LITERAL;
- NUM_DOUBLE_LITERAL;
- NUM_FLOAT_LITERAL;
-}
-
- at lexer::header {
- package org.hibernate.sql.ast.ordering;
-}
-
- at lexer::members {
-}
-
- at parser::header {
- package org.hibernate.sql.ast.ordering;
-
- import org.hibernate.sql.Template;
- import org.hibernate.dialect.function.SQLFunction;
-}
-
@parser::members {
/**
- * Process the given node as a quote identifier. These need to be quoted in the dialect-specific way.
+ * A check to see if the text represents a known function name (in addition to the
+ * set of known {@link #standardFunction "standard"} functions. This is only needed in the
+ * case of seeing a dot-ident structure which is not followed by a paren; such structures
+ * which are followed by a paren are explicitly assumed to be a function name.
*
- * @param ident The quoted-identifier node.
+ * @param text The text to check as a function name.
*
- * @return The processed node.
- */
- protected CommonTree quotedIdentifier(CommonTree ident) {
- // here we assume single-quote as the identifier quote character...
- return createTreeNode( IDENTIFIER, Template.TEMPLATE + ".'" + ident.getText() + "'" );
- }
-
-
- /**
- * Process the given node as a quote string.
+ * @return True if the text is a known function name, false otherwise.
*
- * @param token The quoted string. This is used from within function param recognition, and represents a
- * SQL-quoted string.
- *
- * @return The processed node.
+ * @see #standardFunction
*/
- protected CommonTree quotedString(Token token) {
- return createTreeNode( STRING_LITERAL, "'" + token.getText() + "'" );
+ protected boolean isFunctionName(String text) {
+ // by default, assume it is not
+ return false;
}
/**
- * A check to see if the text of the given node represents a known function name.
+ * A check to see if the text represents a mapped property name.
*
- * @param token The node whose text we want to check.
+ * @param text The text to check as a property name.
*
- * @return True if the node's text is a known function name, false otherwise.
- *
- * @see org.hibernate.dialect.function.SQLFunctionRegistry
+ * @return True if the text is a mapped property name, false otherwise.
*/
- protected boolean isFunctionName(Token token) {
+ protected boolean isPropertyName(String text) {
+ // by default, assume it is not
return false;
}
/**
- * Process the given node as a function name. Differs from {@link #resolveFunction(org.antlr.runtime.tree.CommonTree)
- * specifically in that here we are expecting just a function name without parens or arguments.
+ * Given a property, resolve it's {@link #COLUMN} or {@link #VECTOR_EXPR} tree.
*
- * @param token The token representing the function name.
+ * @param propertyTree The tree representing the property name.
*
- * @return The processed node.
+ * @return The column(s) tree.
*/
- protected CommonTree resolveFunction(Token token) {
- return resolveFunction( new CommonTree( token ) );
+ protected CommonTree buildPropertyColumns(CommonTree propertyTree) {
+ throw new UnsupportedOperationException( "must be overridden!" );
}
- /**
- * Process the given node as a function.
- *
- * @param tree The node representing the function invocation (including parameters as subtree components).
- *
- * @return The processed node.
- */
- protected CommonTree resolveFunction(CommonTree tree) {
- Tree argumentList = tree.getChild( 0 );
- assert argumentList == null || "{param list}".equals( argumentList.getText() );
-
- String text = tree.getText();
- int count = argumentList == null ? 0 : argumentList.getChildCount();
- if ( count > 0 ) {
- text += '(';
- for ( int i = 0; i < count; i++ ) {
- Tree argument = argumentList.getChild( i );
- text += argument.getText();
- if ( i < count ) {
- text += ", ";
- }
- }
- text += ')';
- }
- return createTreeNode( IDENTIFIER, text );
- }
-
- protected CommonTree resolveIdent(Token token) {
- return resolveIdent( new CommonTree( token ) );
- }
-
- /**
- * Process the given node as an IDENTIFIER. 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 CommonTree resolveIdent(CommonTree ident) {
- return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + ident.getText() );
- }
-
- private boolean validateIdentifierAsKeyword(String text) {
- return validateLT( 1, text );
+ private boolean validateSoftKeyword(String text) {
+ return validateLT(1, text);
}
private boolean validateLT(int offset, String text) {
String text2Validate = retrieveLT( offset );
- return text2Validate == null ? false : text2Validate.equalsIgnoreCase(text);
+ return text2Validate == null ? false : text2Validate.equalsIgnoreCase( text );
}
private String retrieveLT(int offset) {
@@ -172,10 +98,6 @@
Token token = input.LT(offset);
return token == null ? null : token.getText();
}
-
- protected CommonTree createTreeNode(int type, String text) {
- return new CommonTree( new CommonToken( type, text ) );
- }
}
@@ -185,17 +107,19 @@
/**
* Main recognition rule for this grammar
*/
-orderByFragment
- : sortSpecification ( ',' sortSpecification )* -> ^( ORDER_BY sortSpecification+ )
- ;
+orderByFragment :
+ sortSpecification ( COMMA sortSpecification )*
+ -> ^( ORDER_BY sortSpecification+ )
+;
/**
- * Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>, which is essentially each thing upon which
- * the results should be sorted.
+ * Reconition rule for what ANSI SQL terms the <tt>sort specification</tt>. These are the atomic elements of the
+ * <tt>ORDER BY</tt> list pieces.
*/
-sortSpecification
- : sortKey collationSpecification? orderingSpecification? -> ^( SORT_SPEC sortKey collationSpecification? orderingSpecification? )
+sortSpecification :
+ sortKey collationSpecification? orderingSpecification?
+ -> ^( SORT_SPEC sortKey collationSpecification? orderingSpecification? )
;
@@ -203,213 +127,441 @@
* 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
- : expression -> ^( SORT_KEY expression )
+sortKey :
+ expression
;
/**
* Reconition rule what this grammar recognizes as valid <tt>sort key</tt>.
*/
expression
- : hardQuoteExpression
- | ( IDENTIFIER ('.' IDENTIFIER)* OPEN_PAREN ) => functionCall
- | simplePropertyPath
- | IDENTIFIER -> {isFunctionName($IDENTIFIER)}? { resolveFunction( $IDENTIFIER ) }
- -> { resolveIdent( $IDENTIFIER ) }
+ : QUOTED_IDENTIFIER -> ^( COLUMN[$QUOTED_IDENTIFIER] ALIAS_REF[Template.TEMPLATE] QUOTED_IDENTIFIER[$QUOTED_IDENTIFIER] )
+ // we treat the so-called standard functions differently because they are handled differently by the HQL lexer which we also use here...
+ | standardFunction
+ // not identDotIdentStructure because we dont want QUOTED_IDENTIFIERs is here
+ | ( IDENTIFIER ( DOT IDENTIFIER )* LEFT_PAREN ) => generalFunction
+ // otherwise we fully expect a dot-identifier series, and then we just need to decode the semantic of that structure
+ | identDotIdentStructure
+ -> { ( isFunctionName($identDotIdentStructure.text) ) }?
+ // we have a function with parens (thus no args)
+ ^( GENERAL_FUNCTION_CALL[$identDotIdentStructure.start,$identDotIdentStructure.text] )
+ -> { ( isPropertyName($identDotIdentStructure.text) ) }?
+ // we have a reference to a mapped property
+ { buildPropertyColumns( $identDotIdentStructure.tree ) }
+ -> { ( $identDotIdentStructure.tree.getType() == DOT ) }?
+ // we have a reference to a column which is already qualified
+ identDotIdentStructure
+ ->
+ // we have a reference to a column which is not qualified
+ ^( COLUMN ALIAS_REF[Template.TEMPLATE] IDENTIFIER[$identDotIdentStructure.start,$identDotIdentStructure.text] )
+ ;
+
+fragment
+identifier
+ : IDENTIFIER
+ | QUOTED_IDENTIFIER
+ ;
+
+fragment
+identDotIdentStructure
+ : IDENTIFIER ( DOT^ identifier )*
+ ;
+
+standardFunction
+ : castFunction
+ | concatFunction
+ | substringFunction
+ | trimFunction
+ | upperFunction
+ | lowerFunction
+ | lengthFunction
+ | locateFunction
+ | absFunction
+ | sqrtFunction
+ | modFunction
+ | currentDateFunction
+ | currentTimeFunction
+ | currentTimestampFunction
+ | extractFunction
+ | positionFunction
+ | charLengthFunction
+ | octetLengthFunction
+ | bitLengthFunction
;
-hardQuoteExpression
- at after { $tree = quotedIdentifier( $tree ); }
- : HARD_QUOTE IDENTIFIER HARD_QUOTE -> IDENTIFIER
+castFunction
+ : cast_keyword^ LEFT_PAREN! expression as_keyword! dataType RIGHT_PAREN!
;
-/**
- * Recognition rule for a function call
- */
-functionCall
- at after { $tree = resolveFunction( $tree ); }
- : functionName OPEN_PAREN functionParameterList CLOSE_PAREN -> ^( functionName functionParameterList )
+fragment
+dataType
+ : IDENTIFIER
;
-/**
- * A function-name is an IDENTIFIER followed by zero or more (DOT IDENTIFIER) sequences
- */
-functionName returns [String nameText]
- : i=IDENTIFIER { $nameText = $i.text; } ( '.' i=IDENTIFIER { $nameText += ( '.' + $i.text ); } )+
+concatFunction
+ : concat_keyword^ LEFT_PAREN! expression ( COMMA! expression )+ RIGHT_PAREN!
;
+substringFunction
+ : substring_keyword^ LEFT_PAREN! expression COMMA! expression ( COMMA! expression)? RIGHT_PAREN!
+ ;
+
+trimFunction
+ : trim_keyword LEFT_PAREN trimOperands RIGHT_PAREN
+ -> ^(trim_keyword trimOperands)
+ ;
+
+fragment
+trimOperands
+options{
+k=2;
+}
+ at init {boolean hasSecondExpression = false;}
+ : trimSpecification from_keyword expression -> ^(trimSpecification STRING_LITERAL[" "] expression)
+ | trimSpecification expression from_keyword expression -> ^(trimSpecification expression+)
+ | from_keyword expression -> ^(BOTH STRING_LITERAL[" "] expression)
+ | cn=expression ( from_keyword expression {hasSecondExpression = true;} )?
+ -> {hasSecondExpression}? ^(BOTH expression+)
+ -> ^(BOTH STRING_LITERAL[" "] $cn)
+ ;
+
+fragment
+trimSpecification
+ : leading_keyword
+ | trailing_keyword
+ | both_keyword
+ ;
+
+upperFunction
+ : upper_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+lowerFunction
+ : lower_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+lengthFunction
+ : length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+locateFunction
+ : locate_keyword^ LEFT_PAREN! expression COMMA! expression ( COMMA! expression )? RIGHT_PAREN!
+ ;
+
+absFunction
+ : abs_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+sqrtFunction
+ : sqrt_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+modFunction
+ : mod_keyword^ LEFT_PAREN! expression COMMA! expression RIGHT_PAREN!
+ ;
+
+currentDateFunction
+ : current_date_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+ ;
+
+currentTimeFunction
+ : current_time_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+ ;
+
+currentTimestampFunction
+ : current_timestamp_keyword ( LEFT_PAREN! RIGHT_PAREN! )?
+ ;
+
+extractFunction
+ : extract_keyword^ LEFT_PAREN! extractField from_keyword! expression RIGHT_PAREN!
+ ;
+
+extractField
+ : datetimeField
+ | timeZoneField
+ ;
+
+datetimeField
+ : nonSecondDatetimeField
+ | second_keyword
+ ;
+
+nonSecondDatetimeField
+ : year_keyword
+ | month_keyword
+ | day_keyword
+ | hour_keyword
+ | minute_keyword
+ ;
+
+timeZoneField
+ : timezone_hour_keyword
+ | timezone_minute_keyword
+ ;
+
+positionFunction
+ : position_keyword^ LEFT_PAREN! expression in_keyword! expression RIGHT_PAREN!
+ ;
+
+charLengthFunction
+ : character_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+octetLengthFunction
+ : octet_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+bitLengthFunction
+ : bit_length_keyword^ LEFT_PAREN! expression RIGHT_PAREN!
+ ;
+
+
+generalFunction
+ : generalFunctionName LEFT_PAREN functionArgumentList RIGHT_PAREN
+ -> ^( GENERAL_FUNCTION_CALL[$generalFunctionName.start,$generalFunctionName.text] functionArgumentList )
+ ;
+
+generalFunctionName :
+ IDENTIFIER ( DOT IDENTIFIER )+
+;
+
/**
- * Recognition rule used to "wrap" all function parameters into an EXPR_LIST node
+ * Recognition rule used to "wrap" all function arguments into a GENERAL_FUNCTION_ARGUMENTS node
*/
-functionParameterList
- : functionParameter ( COMMA functionParameter )* -> ^( EXPR_LIST functionParameter+ )
- ;
+functionArgumentList :
+ functionArgument ( COMMA functionArgument )*
+ -> ^( GENERAL_FUNCTION_ARGUMENTS functionArgument+ )
+;
/**
* Recognized function parameters.
*/
-functionParameter :
+functionArgument :
expression
- | numericLiteral
- | qs=STRING_LITERAL -> { quotedString( $qs ) }
+ | literal
;
-numericLiteral
- : HEX_LITERAL
- | OCTAL_LITERAL
- | DECIMAL_LITERAL
- | FLOATING_POINT_LITERAL
+literal
+ : numeric_literal
+ | HEX_LITERAL
+ | OCTAL_LITERAL
+ | CHARACTER_LITERAL
+ | STRING_LITERAL
;
+numeric_literal
+ : INTEGER_LITERAL
+ | DECIMAL_LITERAL
+ | FLOATING_POINT_LITERAL
+ ;
/**
* 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! :
- collateKeyword collationName -> { createTreeNode(COLLATE, $collationName.text) }
+collationSpecification!
+ : collateKeyword collationName
+ -> COLLATE[$collateKeyword.start,$collationName.text]
;
-collateKeyword
- : {(validateIdentifierAsKeyword("collate"))}?=> id=IDENTIFIER
+collateKeyword :
+ {(validateSoftKeyword("collate"))}?=> id=IDENTIFIER
-> COLLATE[$id]
+;
- ;
-
/**
* The collation name wrt {@link #collationSpecification}. Namely, the character-set.
*/
-collationName
- : IDENTIFIER
- ;
+collationName :
+ IDENTIFIER
+;
/**
* Reconition rule for what ANSI SQL terms the <tt>ordering specification</tt>; <tt>ASCENDING</tt> or
* <tt>DESCENDING</tt>.
*/
-orderingSpecification!
- : ( 'asc' | 'ascending' ) -> { createTreeNode(ORDER_SPEC,"asc") }
- | ( 'desc' | 'descending') -> { createTreeNode(ORDER_SPEC,"desc" ) }
+orderingSpecification! :
+ ascending_keyword
+ -> ORDER_SPEC[$ascending_keyword.start,"asc"]
+ | descending_keyword
+ -> ORDER_SPEC[$descending_keyword.start,"desc"]
+;
+
+
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Soft-keyword handling rules
+// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+abs_keyword
+ : {(validateSoftKeyword("abs"))}?=> id=IDENTIFIER
+ -> ABS[$id]
;
-/**
- * A simple-property-path is an IDENTIFIER followed by one or more (DOT IDENTIFIER) sequences
- */
-simplePropertyPath
- at after { $tree = resolveIdent($tree); }
- : p=simplePropertyPathText -> { createTreeNode(IDENTIFIER, $p.pathText) }
+as_keyword
+ : {(validateSoftKeyword("as"))}?=> id=IDENTIFIER
+ -> AS[$id]
;
-simplePropertyPathText returns [String pathText]
- : i=IDENTIFIER { $pathText = $i.text; } ( '.' i=IDENTIFIER { $pathText += ( '.' + $i.text ); } )+
+ascending_keyword :
+ {(validateSoftKeyword("ascending") || validateSoftKeyword("asc"))}?=> IDENTIFIER
+;
+
+bit_length_keyword
+ : {(validateSoftKeyword("bit_length"))}?=> id=IDENTIFIER
+ -> BIT_LENGTH[$id]
;
+both_keyword
+ : {(validateSoftKeyword("both"))}?=> id=IDENTIFIER
+ -> BOTH[$id]
+ ;
+cast_keyword
+ : {(validateSoftKeyword("cast"))}?=> id=IDENTIFIER
+ -> CAST[$id]
+ ;
-// Lexer rules ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+character_length_keyword
+ : {(validateSoftKeyword("character_length") || validateSoftKeyword("char_length"))}?=> id=IDENTIFIER
+ -> CHARACTER_LENGTH[$id]
+ ;
-WS
- : (SPACE | EOL | '\u000C') { $channel=HIDDEN; }
- ;
+concat_keyword
+ : {(validateSoftKeyword("concat"))}?=> id=IDENTIFIER
+ -> CONCAT[$id]
+ ;
-fragment
-EOL
- : ( '\r' (options{greedy=true;}: '\n')? | '\n' )
- ;
+current_date_keyword
+ : {(validateSoftKeyword("current_date"))}?=> id=IDENTIFIER
+ -> CURRENT_DATE[$id]
+ ;
-fragment
-SPACE
- : ' '
- | '\t'
- ;
+current_time_keyword
+ : {(validateSoftKeyword("current_time"))}?=> id=IDENTIFIER
+ -> CURRENT_TIME[$id]
+ ;
-OPEN_PAREN
- : '('
- ;
-CLOSE_PAREN
- : ')'
- ;
+current_timestamp_keyword
+ : {(validateSoftKeyword("current_timestamp"))}?=> id=IDENTIFIER
+ -> CURRENT_TIMESTAMP[$id]
+ ;
-COMMA
- : ','
- ;
+day_keyword
+ : {(validateSoftKeyword("day"))}?=> id=IDENTIFIER
+ -> DAY[$id]
+ ;
-HARD_QUOTE
- : '`'
- ;
+descending_keyword :
+ {(validateSoftKeyword("descending") || validateSoftKeyword("desc"))}?=> IDENTIFIER
+;
-INTEGER_LITERAL
- : (
- '0'
- | '1'..'9' ('0'..'9')*
- )
- ;
+extract_keyword
+ : {(validateSoftKeyword("extract"))}?=> id=IDENTIFIER
+ -> EXTRACT[$id]
+ ;
-DECIMAL_LITERAL : ('0' | '1'..'9' '0'..'9'*) INTEGER_TYPE_SUFFIX ;
+from_keyword
+ : {(validateSoftKeyword("from"))}?=> id=IDENTIFIER
+ -> FROM[$id]
+ ;
-HEX_LITERAL
- : '0' ('x'|'X') HEX_DIGIT+ INTEGER_TYPE_SUFFIX?
- ;
+hour_keyword
+ : {(validateSoftKeyword("hour"))}?=> id=IDENTIFIER
+ -> HOUR[$id]
+ ;
-OCTAL_LITERAL : '0' ('0'..'7')+ INTEGER_TYPE_SUFFIX? ;
+in_keyword
+ : {(validateSoftKeyword("in"))}?=> id=IDENTIFIER
+ -> IN[$id]
+ ;
+leading_keyword
+ : {(validateSoftKeyword("leading"))}?=> id=IDENTIFIER
+ -> LEADING[$id]
+ ;
+length_keyword
+ : {(validateSoftKeyword("length"))}?=> id=IDENTIFIER
+ -> LENGTH[$id]
+ ;
-fragment
-HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;
+locate_keyword
+ : {(validateSoftKeyword("locate"))}?=> id=IDENTIFIER
+ -> LOCATE[$id]
+ ;
-fragment
-INTEGER_TYPE_SUFFIX : ('l'|'L') ;
+lower_keyword
+ : {(validateSoftKeyword("lower"))}?=> id=IDENTIFIER
+ -> LOWER[$id]
+ ;
-FLOATING_POINT_LITERAL
- : ('0'..'9')+ '.' ('0'..'9')* EXPONENT? FLOAT_TYPE_SUFFIX?
- | '.' ('0'..'9')+ EXPONENT? FLOAT_TYPE_SUFFIX?
- | ('0'..'9')+ EXPONENT FLOAT_TYPE_SUFFIX?
- | ('0'..'9')+ FLOAT_TYPE_SUFFIX
- ;
+minute_keyword
+ : {(validateSoftKeyword("minute"))}?=> id=IDENTIFIER
+ -> MINUTE[$id]
+ ;
-fragment
-EXPONENT : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
+mod_keyword
+ : {(validateSoftKeyword("mod"))}?=> id=IDENTIFIER
+ -> MOD[$id]
+ ;
-fragment
-FLOAT_TYPE_SUFFIX : ('f'|'F'|'d'|'D') ;
+month_keyword
+ : {(validateSoftKeyword("month"))}?=> id=IDENTIFIER
+ -> MONTH[$id]
+ ;
+octet_length_keyword
+ : {(validateSoftKeyword("octet_length"))}?=> id=IDENTIFIER
+ -> OCTET_LENGTH[$id]
+ ;
-STRING_LITERAL
- : '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\''
- ;
+position_keyword
+ : {(validateSoftKeyword("position"))}?=> id=IDENTIFIER
+ -> POSITION[$id]
+ ;
-fragment
-ESCAPE_SEQUENCE
- : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
- | UNICODE_ESCAPE
- | OCTAL_ESCAPE
- ;
+second_keyword
+ : {(validateSoftKeyword("second"))}?=> id=IDENTIFIER
+ -> SECOND[$id]
+ ;
-fragment
-OCTAL_ESCAPE
- : '\\' ('0'..'3') ('0'..'7') ('0'..'7')
- | '\\' ('0'..'7') ('0'..'7')
- | '\\' ('0'..'7')
- ;
+sqrt_keyword
+ : {(validateSoftKeyword("sqrt"))}?=> id=IDENTIFIER
+ -> SQRT[$id]
+ ;
-fragment
-UNICODE_ESCAPE
- : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
- ;
+substring_keyword
+ : {(validateSoftKeyword("substring"))}?=> id=IDENTIFIER
+ -> SUBSTRING[$id]
+ ;
-IDENTIFIER
- : IDENTIFIER_START_FRAGMENT (IDENTIFER_FRAGMENT)*
+timezone_hour_keyword
+ : {(validateSoftKeyword("timezone_hour"))}?=> id=IDENTIFIER
+ -> TIMEZONE_HOUR[$id]
;
-fragment
-IDENTIFIER_START_FRAGMENT
- : ('a'..'z'|'A'..'Z'|'_'|'$'|'\u0080'..'\ufffe')
- ;
+timezone_minute_keyword
+ : {(validateSoftKeyword("timezone_minute"))}?=> id=IDENTIFIER
+ -> TIMEZONE_MINUTE[$id]
+ ;
-fragment
-IDENTIFER_FRAGMENT
- : IDENTIFIER_START_FRAGMENT
- | '0'..'9'
- ;
+trailing_keyword
+ : {(validateSoftKeyword("trailing"))}?=> id=IDENTIFIER
+ -> TRAILING[$id]
+ ;
+trim_keyword
+ : {(validateSoftKeyword("trim"))}?=> id=IDENTIFIER
+ -> TRIM[$id]
+ ;
+
+upper_keyword
+ : {(validateSoftKeyword("upper"))}?=> id=IDENTIFIER
+ -> UPPER[$id]
+ ;
+
+year_keyword
+ : {(validateSoftKeyword("year"))}?=> id=IDENTIFIER
+ -> YEAR[$id]
+ ;
+
Modified: core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g
===================================================================
--- core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/antlr3/org/hibernate/sql/ast/phase/hql/parse/HQLLexer.g 2009-04-24 21:05:09 UTC (rev 16437)
@@ -19,6 +19,7 @@
ENTITY_PERSISTER_REF;
FILTER;
GENERAL_FUNCTION_CALL;
+ GENERAL_FUNCTION_ARGUMENTS;
GROUPING_VALUE;
IN_LIST;
INSERTABILITY_SPEC;
@@ -32,6 +33,7 @@
NOT_IN;
NOT_LIKE;
NOT_MEMBER_OF;
+ ORDER_SPEC;
PATH;
PERSISTER_JOIN;
PERSISTER_SPACE;
@@ -122,7 +124,6 @@
ON;
OR;
ORDER_BY;
- ORDER_SPEC;
OUTER;
POSITION;
PROPERTIES;
@@ -233,7 +234,6 @@
STRING_LITERAL
: '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"'
| ('\'' ( ESCAPE_SEQUENCE | ~('\\'|'\'') )* '\'')+
- | '`' ( ESCAPE_SEQUENCE | ~('\\'|'`') )* '`'
;
fragment
@@ -320,6 +320,10 @@
: ('a'..'z'|'A'..'Z'|'_'|'$'|'\u0080'..'\ufffe')('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|'\u0080'..'\ufffe')*
;
+QUOTED_IDENTIFIER
+ : '`' ( ESCAPE_SEQUENCE | ~('\\'|'`') )* '`'
+ ;
+
LEFT_PAREN
: '('
;
Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateToken.java 2009-04-24 21:05:09 UTC (rev 16437)
@@ -26,7 +26,6 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
-
package org.hibernate.sql.ast.common;
import org.antlr.runtime.CommonToken;
@@ -37,34 +36,34 @@
* Models the token-type/text portion of an Antlr tree for a specific node in said tree.
*
* @author Steve Ebersole
+ * @author Alexandre Porcelli
*/
public class HibernateToken extends CommonToken {
- public HibernateToken(int type) {
- super(type);
- }
+ public HibernateToken(int type) {
+ super( type );
+ }
- public HibernateToken(CharStream input, int type, int channel, int start,
- int stop) {
- super(input, type, channel, start, stop);
- }
+ public HibernateToken(CharStream input, int type, int channel, int start, int stop) {
+ super( input, type, channel, start, stop );
+ }
- public HibernateToken(int type, String text) {
- super(type, text);
- }
+ public HibernateToken(int type, String text) {
+ super( type, text );
+ }
- /**
- * Constructor that preserves the char offset
- *
- * @param oldToken
- */
- public HibernateToken(Token oldToken) {
- super(oldToken);
- if (null != oldToken
- && (oldToken.getClass().equals(CommonToken.class) || oldToken
- .getClass().equals(HibernateToken.class))) {
- start = ((CommonToken) oldToken).getStartIndex();
- stop = ((CommonToken) oldToken).getStopIndex();
- }
- }
+ /**
+ * Constructor that preserves the char offset
+ *
+ * @param oldToken A token to use to template the creation of this new one.
+ */
+ public HibernateToken(Token oldToken) {
+ super( oldToken );
+ if ( null != oldToken
+ && ( oldToken.getClass().equals( CommonToken.class ) || oldToken
+ .getClass().equals( HibernateToken.class ) ) ) {
+ start = ( ( CommonToken ) oldToken ).getStartIndex();
+ stop = ( ( CommonToken ) oldToken ).getStopIndex();
+ }
+ }
}
Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/common/HibernateTree.java 2009-04-24 21:05:09 UTC (rev 16437)
@@ -36,6 +36,7 @@
* todo : javadocs
*
* @author Steve Ebersole
+ * @author Alexandre Porcelli
*/
public class HibernateTree extends CommonTree {
/**
Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentParser.java 2009-04-24 21:05:09 UTC (rev 16437)
@@ -24,172 +24,86 @@
*/
package org.hibernate.sql.ast.ordering;
-import java.util.ArrayList;
-
-import org.hibernate.sql.Template;
-import org.hibernate.dialect.function.SQLFunction;
-import org.hibernate.util.StringHelper;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.antlr.runtime.TokenStream;
-import org.antlr.runtime.CommonToken;
import org.antlr.runtime.tree.CommonTree;
-import org.antlr.runtime.tree.Tree;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.hibernate.sql.ast.common.HibernateTree;
+import org.hibernate.sql.Template;
+
/**
* Extension of the Antlr-generated parser for the purpose of adding our custom parsing behavior.
*
* @author Steve Ebersole
*/
-public class OrderByFragmentParser extends OrderByParserParser {
+public class OrderByFragmentParser extends OrderByParser {
private static final Logger log = LoggerFactory.getLogger( OrderByFragmentParser.class );
-
private final TranslationContext context;
- private int traceDepth = 0;
+ private final String openQuoteChar;
+ private final String closeQuoteChar;
+
public OrderByFragmentParser(TokenStream lexer, TranslationContext context) {
super( lexer );
this.context = context;
+
+ this.openQuoteChar = Character.toString( context.getDialect().openQuote() );
+ this.closeQuoteChar = Character.toString( context.getDialect().closeQuote() );
}
-//
-//
-// // handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-// public void traceIn(String ruleName) throws TokenStreamException {
-// if ( inputState.guessing > 0 ) {
-// return;
-// }
-// String prefix = StringHelper.repeat( "-", (traceDepth++ * 2) ) + "->";
-// trace( prefix + ruleName );
-// }
-//
-// public void traceOut(String ruleName) throws TokenStreamException {
-// if ( inputState.guessing > 0 ) {
-// return;
-// }
-// String prefix = "<-" + StringHelper.repeat( "-", (--traceDepth * 2) );
-// trace( prefix + ruleName );
-// }
-//
-// private void trace(String msg) {
-// log.trace( msg );
-// }
-protected CommonTree quotedIdentifier(CommonTree ident) {
- return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + context.getDialect().quote( '`' + ident.getText() + '`' ) );
-}
-
- protected CommonTree quotedString(CommonTree ident) {
- return createTreeNode( IDENTIFIER, context.getDialect().quote( ident.getText() ) );
+ @Override
+ protected boolean isFunctionName(String text) {
+ log.trace( "Checking function name [" + text + "]" );
+ return context.getSqlFunctionRegistry().hasFunction( text );
}
- protected boolean isFunctionName(CommonToken token) {
- return context.getSqlFunctionRegistry().hasFunction( token.getText() );
- }
-
- protected CommonTree resolveFunction(CommonTree tree) {
- Tree argumentList = tree.getChild( 0 );
- assert "{param list}".equals( argumentList.getText() );
-
- final String functionName = tree.getText();
- final SQLFunction function = context.getSqlFunctionRegistry().findSQLFunction( functionName );
-
- if ( function == null ) {
- // If the function is not registered with the session factory we just need to render it as-is
- // including its arguments...
- String text = functionName;
- int count = argumentList.getChildCount();
- if ( count > 0 ) {
- text += '(';
- for ( int i = 0; i < count; i++ ) {
- Tree argument = argumentList.getChild( i );
- text += argument.getText();
- if ( i < count ) {
- text += ", ";
- }
- }
- text += ')';
- }
- return createTreeNode( IDENTIFIER, text );
+ @Override
+ protected boolean isPropertyName(String text) {
+ log.trace( "Checking property name [" + text + "]" );
+ try {
+ return context.getColumnMapper().map( text ) != null;
}
- else {
- ArrayList expressions = new ArrayList();
- for ( int i = 0; i < argumentList.getChildCount(); i++ ) {
- expressions.add( argumentList.getChild( i ).getText() );
- }
- final String text = function.render( expressions, context.getSessionFactory() );
- return createTreeNode( IDENTIFIER, text );
+ catch ( Throwable t ) {
+ return false;
}
}
- protected CommonTree resolveIdent(CommonTree ident) {
- String text = ident.getText();
- String[] replacements;
- try {
- replacements = context.getColumnMapper().map( text );
+ @Override
+ protected CommonTree buildPropertyColumns(CommonTree propertyTree) {
+ final String text = extractPropertyName( propertyTree );
+ String[] columns = context.getColumnMapper().map( text );
+ if ( columns.length == 1 ) {
+ return buildColumn( columns[0] );
}
- catch( Throwable t ) {
- replacements = null;
+ else {
+ HibernateTree vector = new HibernateTree( VECTOR_EXPR );
+ for ( String column : columns ) {
+ vector.addChild( buildColumn( column ) );
+ }
+ return vector;
}
+ }
- if ( replacements == null || replacements.length == 0 ) {
- return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + text );
+ private CommonTree buildColumn(String columnName) {
+ // ugh
+ HibernateTree columnNameNode;
+ if ( columnName.startsWith( openQuoteChar ) && columnName.endsWith( closeQuoteChar ) ) {
+ columnName = columnName.substring( 1, columnName.length() - 1 );
+ columnNameNode = new HibernateTree( QUOTED_IDENTIFIER, columnName );
}
- else if ( replacements.length == 1 ) {
- return createTreeNode( IDENTIFIER, Template.TEMPLATE + "." + replacements[0] );
- }
else {
- final CommonTree root = createTreeNode( IDENT_LIST, "{ident list}" );
- for ( int i = 0; i < replacements.length; i++ ) {
- final String identText = Template.TEMPLATE + '.' + replacements[i];
- root.addChild( createTreeNode( IDENTIFIER, identText ) );
- }
- return root;
+ columnNameNode = new HibernateTree( IDENTIFIER, columnName );
}
+
+ HibernateTree tree = new HibernateTree( COLUMN );
+ tree.addChild( new HibernateTree( ALIAS_REF, Template.TEMPLATE ) );
+ tree.addChild( columnNameNode );
+
+ return tree;
}
-//
-// /**
-// * {@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;
-// }
+
+ private String extractPropertyName(CommonTree propertyTree) {
+ return propertyTree.getText();
+ }
}
Modified: core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java
===================================================================
--- core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java 2009-04-24 15:30:34 UTC (rev 16436)
+++ core/branches/antlr3/src/main/java/org/hibernate/sql/ast/ordering/OrderByFragmentTranslator.java 2009-04-24 21:05:09 UTC (rev 16437)
@@ -24,15 +24,13 @@
*/
package org.hibernate.sql.ast.ordering;
-import java.io.StringReader;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.sql.ast.util.TreePrinter;
-import org.antlr.runtime.CharStream;
-import org.antlr.runtime.TokenStream;
+import org.hibernate.sql.ast.phase.hql.parse.HQLLexer;
+
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.Tree;
@@ -59,13 +57,13 @@
* @return The translated fragment.
*/
public String render(String fragment) {
- OrderByParserLexer lexer = new OrderByParserLexer( new ANTLRStringStream( fragment ) );
+ HQLLexer lexer = new HQLLexer( new ANTLRStringStream( fragment ) );
OrderByFragmentParser parser = new OrderByFragmentParser( new CommonTokenStream( lexer ), context );
try {
Tree tree = ( Tree ) parser.orderByFragment().getTree();
if ( log.isTraceEnabled() ) {
- TreePrinter printer = new TreePrinter( OrderByParserParser.class );
+ TreePrinter printer = new TreePrinter( OrderByParser.class );
log.trace( printer.renderAsString( tree, "--- {order-by fragment} ---" ) );
}
}
Added: core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java
===================================================================
--- core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java (rev 0)
+++ core/branches/antlr3/src/test/java/org/hibernate/sql/ast/ordering/OrderByParserTest.java 2009-04-24 21:05:09 UTC (rev 16437)
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2009, 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.ast.ordering;
+
+import java.util.Collections;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import org.hibernate.persister.entity.PropertyMapping;
+import org.hibernate.QueryException;
+import org.hibernate.Hibernate;
+import org.hibernate.HibernateException;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.function.SQLFunctionRegistry;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.sql.ast.phase.hql.parse.HQLLexer;
+import org.hibernate.sql.ast.util.ASTUtil;
+import org.hibernate.sql.ast.util.TreePrinter;
+import org.hibernate.sql.Template;
+import org.hibernate.type.Type;
+
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.tree.Tree;
+import org.antlr.runtime.tree.CommonTree;
+
+/**
+ * Assertions against the structures generated by various paths of the "order-by" mapping fragment parser.
+ *
+ * @author Steve Ebersole
+ */
+public class OrderByParserTest extends TestCase {
+ public static final PropertyMapping STANDARD_PROPERTY_MAPPING = new PropertyMapping() {
+ public String[] toColumns(String propertyName) throws QueryException, UnsupportedOperationException {
+ 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 {
+ throw new UnsupportedOperationException( "toType(propertyName) not supported here" );
+ }
+
+ public String[] toColumns(String alias, String propertyName) throws QueryException {
+ throw new UnsupportedOperationException( "toColumns(alias,propertyName) not supported here" );
+ }
+
+ public Type getType() {
+ throw new UnsupportedOperationException( "getType() not supported here" );
+ }
+ };
+
+ public static final ColumnMapper STANDARD_MAPPER = new ColumnMapper() {
+ public String[] map(String reference) {
+ return STANDARD_PROPERTY_MAPPING.toColumns( reference );
+ }
+ };
+
+ public OrderByParserTest() {
+ }
+
+ public OrderByParserTest(String name) {
+ super( name );
+ }
+
+ public void testSQLReferences() {
+ String fragment = "sql asc, sql desc";
+ CommonTree tree = parse( fragment );
+
+ assertTokenType( HQLLexer.ORDER_BY, tree );
+ assertEquals( 2, tree.getChildCount() );
+
+ Tree firstSortSpec = tree.getChild( 0 );
+ assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec );
+ assertEquals( 2, firstSortSpec.getChildCount() );
+ assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+ assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+ assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+ assertTokenType( HQLLexer.IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+ assertEquals( "sql", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+ assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+ assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+
+ Tree secondSortSpec = tree.getChild( 1 );
+ assertTokenType( HQLLexer.SORT_SPEC, secondSortSpec.getType() );
+ assertEquals( 2, secondSortSpec.getChildCount() );
+ assertTokenType( HQLLexer.COLUMN, secondSortSpec.getChild( 0 ) );
+ assertTokenType( HQLLexer.ALIAS_REF, secondSortSpec.getChild( 0 ).getChild( 0 ) );
+ assertEquals( Template.TEMPLATE, secondSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+ assertTokenType( HQLLexer.IDENTIFIER, secondSortSpec.getChild( 0 ).getChild( 1 ) );
+ assertEquals( "sql", secondSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+ assertTokenType( HQLLexer.ORDER_SPEC, secondSortSpec.getChild( 1 ) );
+ assertEquals( "desc", secondSortSpec.getChild( 1 ).getText() );
+ }
+
+ public void testQuotedSQLReferences() {
+ String fragment = "`sql` asc, `sql` desc";
+ CommonTree tree = parse( fragment );
+
+ assertTokenType( HQLLexer.ORDER_BY, tree.getType() );
+ assertEquals( 2, tree.getChildCount() );
+
+ Tree firstSortSpec = tree.getChild( 0 );
+ assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec.getType() );
+ assertEquals( 2, firstSortSpec.getChildCount() );
+ assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+ assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+ assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+ assertTokenType( HQLLexer.QUOTED_IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+ assertEquals( "`sql`", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+ assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+ assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+
+ Tree secondSortSpec = tree.getChild( 1 );
+ assertTokenType( HQLLexer.SORT_SPEC, secondSortSpec.getType() );
+ assertEquals( 2, secondSortSpec.getChildCount() );
+ assertTokenType( HQLLexer.COLUMN, secondSortSpec.getChild( 0 ) );
+ assertTokenType( HQLLexer.ALIAS_REF, secondSortSpec.getChild( 0 ).getChild( 0 ) );
+ assertEquals( Template.TEMPLATE, secondSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+ assertTokenType( HQLLexer.QUOTED_IDENTIFIER, secondSortSpec.getChild( 0 ).getChild( 1 ) );
+ assertEquals( "`sql`", secondSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+ assertTokenType( HQLLexer.ORDER_SPEC, secondSortSpec.getChild( 1 ) );
+ assertEquals( "desc", secondSortSpec.getChild( 1 ).getText() );
+ }
+
+ public void testPropertyReference() {
+ String fragment = "property asc, property desc";
+ CommonTree tree = parse( fragment );
+
+ assertTokenType( HQLLexer.ORDER_BY, tree.getType() );
+ assertEquals( 2, tree.getChildCount() );
+
+ Tree firstSortSpec = tree.getChild( 0 );
+ assertTokenType( HQLLexer.SORT_SPEC, firstSortSpec.getType() );
+ assertEquals( 2, firstSortSpec.getChildCount() );
+ assertTokenType( HQLLexer.COLUMN, firstSortSpec.getChild( 0 ) );
+ assertTokenType( HQLLexer.ALIAS_REF, firstSortSpec.getChild( 0 ).getChild( 0 ) );
+ assertEquals( Template.TEMPLATE, firstSortSpec.getChild( 0 ).getChild( 0 ).getText() );
+ assertTokenType( HQLLexer.IDENTIFIER, firstSortSpec.getChild( 0 ).getChild( 1 ) );
+ assertEquals( "prop", firstSortSpec.getChild( 0 ).getChild( 1 ).getText() );
+ assertTokenType( HQLLexer.ORDER_SPEC, firstSortSpec.getChild( 1 ) );
+ assertEquals( "asc", firstSortSpec.getChild( 1 ).getText() );
+ }
+
+ protected CommonTree parse(String orderByFragment) {
+ final Dialect dialect = new HSQLDialect();
+ final SQLFunction myFunction = new SQLFunctionTemplate( Hibernate.STRING, "myFunction($1)" );
+ final SQLFunctionRegistry functionRegistry = new SQLFunctionRegistry( dialect, Collections.singletonMap( "myFunction", myFunction ) );
+ return parse(
+ orderByFragment,
+ new TranslationContext() {
+ public SessionFactoryImplementor getSessionFactory() {
+ return null;
+ }
+
+ public Dialect getDialect() {
+ return dialect;
+ }
+
+ public SQLFunctionRegistry getSqlFunctionRegistry() {
+ return functionRegistry;
+ }
+
+ public ColumnMapper getColumnMapper() {
+ return STANDARD_MAPPER;
+ }
+ }
+ );
+ }
+
+ private static final TreePrinter TREE_PRINTER = new TreePrinter( HQLLexer.class );
+
+ protected CommonTree parse(String orderByFragment, TranslationContext context) {
+ try {
+ HQLLexer lexer = new HQLLexer( new ANTLRStringStream( orderByFragment ) );
+ OrderByFragmentParser parser = new OrderByFragmentParser( new CommonTokenStream( lexer ), context );
+ CommonTree rtn = parser.orderByFragment().tree;
+ // temp
+ System.out.println(
+ TREE_PRINTER.renderAsString( rtn, "fragment" )
+ );
+ return rtn;
+ }
+ catch ( HibernateException e ) {
+ throw e;
+ }
+ catch ( Throwable t ) {
+ throw new HibernateException( "Unable to parse order-by fragment", t );
+ }
+ }
+
+ private static final Map<Integer,String> tokenNameMap = ( Map<Integer,String> ) ASTUtil.generateTokenNameCache( HQLLexer.class );
+
+ public void assertTokenType(int expected, int actual) {
+ if ( expected != actual ) {
+ throw new AssertionFailedError(
+ "expected:<"+ tokenNameMap.get( expected ) +"> but was:<"+ tokenNameMap.get( actual ) +">"
+ );
+ }
+ }
+
+ public void assertTokenType(int expected, Tree tree) {
+ if ( tree == null ) {
+ throw new AssertionFailedError(
+ "expected:<"+ tokenNameMap.get( expected ) +"> but was:<null>"
+ );
+ }
+ assertTokenType( expected, tree.getType() );
+ }
+}
More information about the hibernate-commits
mailing list