428 lines
11 KiB
Plaintext
428 lines
11 KiB
Plaintext
header
|
|
{
|
|
// $Id$
|
|
package org.hibernate.hql.antlr;
|
|
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
}
|
|
/**
|
|
* SQL Generator Tree Parser, providing SQL rendering of SQL ASTs produced by the previous phase, HqlSqlWalker. All
|
|
* syntax decoration such as extra spaces, lack of spaces, extra parens, etc. should be added by this class.
|
|
* <br>
|
|
* This grammar processes the HQL/SQL AST and produces an SQL string. The intent is to move dialect-specific
|
|
* code into a sub-class that will override some of the methods, just like the other two grammars in this system.
|
|
* @author Joshua Davis (joshua@hibernate.org)
|
|
*/
|
|
class SqlGeneratorBase extends TreeParser;
|
|
|
|
options {
|
|
// Note: importVocab and exportVocab cause ANTLR to share the token type numbers between the
|
|
// two grammars. This means that the token type constants from the source tree are the same
|
|
// as those in the target tree. If this is not the case, tree translation can result in
|
|
// token types from the *source* tree being present in the target tree.
|
|
importVocab=HqlSql; // import definitions from "HqlSql"
|
|
exportVocab=Sql; // Call the resulting definitions "Sql"
|
|
buildAST=false; // Don't build an AST.
|
|
}
|
|
|
|
{
|
|
private static Log log = LogFactory.getLog(SqlGeneratorBase.class);
|
|
|
|
/** the buffer resulting SQL statement is written to */
|
|
private StringBuffer buf = new StringBuffer();
|
|
|
|
protected void out(String s) {
|
|
buf.append(s);
|
|
}
|
|
|
|
/**
|
|
* Returns the last character written to the output, or -1 if there isn't one.
|
|
*/
|
|
protected int getLastChar() {
|
|
int len = buf.length();
|
|
if ( len == 0 )
|
|
return -1;
|
|
else
|
|
return buf.charAt( len - 1 );
|
|
}
|
|
|
|
/**
|
|
* Add a aspace if the previous token was not a space or a parenthesis.
|
|
*/
|
|
protected void optionalSpace() {
|
|
// Implemented in the sub-class.
|
|
}
|
|
|
|
protected void out(AST n) {
|
|
out(n.getText());
|
|
}
|
|
|
|
protected void separator(AST n, String sep) {
|
|
if (n.getNextSibling() != null)
|
|
out(sep);
|
|
}
|
|
|
|
protected boolean hasText(AST a) {
|
|
String t = a.getText();
|
|
return t != null && t.length() > 0;
|
|
}
|
|
|
|
protected void fromFragmentSeparator(AST a) {
|
|
// moved this impl into the subclass...
|
|
}
|
|
|
|
protected void nestedFromFragment(AST d,AST parent) {
|
|
// moved this impl into the subclass...
|
|
}
|
|
|
|
protected StringBuffer getStringBuffer() {
|
|
return buf;
|
|
}
|
|
|
|
protected void nyi(AST n) {
|
|
throw new UnsupportedOperationException("Unsupported node: " + n);
|
|
}
|
|
|
|
protected void beginFunctionTemplate(AST m,AST i) {
|
|
// if template is null we just write the function out as it appears in the hql statement
|
|
out(i);
|
|
out("(");
|
|
}
|
|
|
|
protected void endFunctionTemplate(AST m) {
|
|
out(")");
|
|
}
|
|
|
|
protected void commaBetweenParameters(String comma) {
|
|
out(comma);
|
|
}
|
|
}
|
|
|
|
statement
|
|
: selectStatement
|
|
| updateStatement
|
|
| deleteStatement
|
|
| insertStatement
|
|
;
|
|
|
|
selectStatement
|
|
: #(SELECT { out("select "); }
|
|
selectClause
|
|
from
|
|
( #(WHERE { out(" where "); } whereExpr ) )?
|
|
( #(GROUP { out(" group by "); } groupExprs ( #(HAVING { out(" having "); } booleanExpr[false]) )? ) )?
|
|
( #(ORDER { out(" order by "); } orderExprs ) )?
|
|
)
|
|
;
|
|
|
|
// Note: eats the FROM token node, as it is not valid in an update statement.
|
|
// It's outlived its usefulness after analysis phase :)
|
|
// TODO : needed to use conditionList directly here and deleteStatement, as whereExprs no longer works for this stuff
|
|
updateStatement
|
|
: #(UPDATE { out("update "); }
|
|
#( FROM fromTable )
|
|
setClause
|
|
(whereClause)?
|
|
)
|
|
;
|
|
|
|
deleteStatement
|
|
// Note: not space needed at end of "delete" because the from rule included one before the "from" it outputs
|
|
: #(DELETE { out("delete"); }
|
|
from
|
|
(whereClause)?
|
|
)
|
|
;
|
|
|
|
insertStatement
|
|
: #(INSERT { out( "insert " ); }
|
|
i:INTO { out( i ); out( " " ); }
|
|
selectStatement
|
|
)
|
|
;
|
|
|
|
setClause
|
|
// Simply re-use comparisionExpr, because it already correctly defines the EQ rule the
|
|
// way it is needed here; not the most aptly named, but ah
|
|
: #( SET { out(" set "); } comparisonExpr[false] ( { out(", "); } comparisonExpr[false] )* )
|
|
;
|
|
|
|
whereClause
|
|
: #(WHERE { out(" where "); } whereClauseExpr )
|
|
;
|
|
|
|
whereClauseExpr
|
|
: (SQL_TOKEN) => conditionList
|
|
| booleanExpr[ false ]
|
|
;
|
|
|
|
orderExprs
|
|
// TODO: remove goofy space before the comma when we don't have to regression test anymore.
|
|
: ( expr ) (dir:orderDirection { out(" "); out(dir); })? ( {out(", "); } orderExprs)?
|
|
;
|
|
|
|
groupExprs
|
|
// TODO: remove goofy space before the comma when we don't have to regression test anymore.
|
|
: expr ( {out(" , "); } groupExprs)?
|
|
;
|
|
|
|
orderDirection
|
|
: ASCENDING
|
|
| DESCENDING
|
|
;
|
|
|
|
whereExpr
|
|
// Expect the filter subtree, followed by the theta join subtree, followed by the HQL condition subtree.
|
|
// Might need parens around the HQL condition if there is more than one subtree.
|
|
// Put 'and' between each subtree.
|
|
: filters
|
|
( { out(" and "); } thetaJoins )?
|
|
( { out(" and "); } booleanExpr [ true ] )?
|
|
| thetaJoins
|
|
( { out(" and "); } booleanExpr [ true ] )?
|
|
| booleanExpr[false]
|
|
;
|
|
|
|
filters
|
|
: #(FILTERS conditionList )
|
|
;
|
|
|
|
thetaJoins
|
|
: #(THETA_JOINS conditionList )
|
|
;
|
|
|
|
conditionList
|
|
: sqlToken ( { out(" and "); } conditionList )?
|
|
;
|
|
|
|
selectClause
|
|
: #(SELECT_CLAUSE (distinctOrAll)? ( selectColumn )+ )
|
|
;
|
|
|
|
selectColumn
|
|
: p:selectExpr (sc:SELECT_COLUMNS { out(sc); } )? { separator( (sc != null) ? sc : p,", "); }
|
|
;
|
|
|
|
selectExpr
|
|
: e:selectAtom { out(e); }
|
|
| count
|
|
| #(CONSTRUCTOR (DOT | IDENT) ( selectColumn )+ )
|
|
| methodCall
|
|
| aggregate
|
|
| c:constant { out(c); }
|
|
| arithmeticExpr
|
|
| param:PARAM { out(param); }
|
|
| sn:SQL_NODE { out(sn); }
|
|
| { out("("); } selectStatement { out(")"); }
|
|
;
|
|
|
|
count
|
|
: #(COUNT { out("count("); } ( distinctOrAll ) ? countExpr { out(")"); } )
|
|
;
|
|
|
|
distinctOrAll
|
|
: DISTINCT { out("distinct "); }
|
|
| ALL { out("all "); }
|
|
;
|
|
|
|
countExpr
|
|
// Syntacitic predicate resolves star all by itself, avoiding a conflict with STAR in expr.
|
|
: ROW_STAR { out("*"); }
|
|
| simpleExpr
|
|
;
|
|
|
|
selectAtom
|
|
: DOT
|
|
| SQL_TOKEN
|
|
| ALIAS_REF
|
|
| SELECT_EXPR
|
|
;
|
|
|
|
// The from-clause piece is all goofed up. Currently, nodes of type FROM_FRAGMENT
|
|
// and JOIN_FRAGMENT can occur at any level in the FromClause sub-tree. We really
|
|
// should come back and clean this up at some point; which I think will require
|
|
// a post-HqlSqlWalker phase to "re-align" the FromElements in a more sensible
|
|
// manner.
|
|
from
|
|
: #(f:FROM { out(" from "); }
|
|
(fromTable)* )
|
|
;
|
|
|
|
fromTable
|
|
// Write the table node (from fragment) and all the join fragments associated with it.
|
|
: #( a:FROM_FRAGMENT { out(a); } (tableJoin [ a ])* { fromFragmentSeparator(a); } )
|
|
| #( b:JOIN_FRAGMENT { out(b); } (tableJoin [ b ])* { fromFragmentSeparator(b); } )
|
|
;
|
|
|
|
tableJoin [ AST parent ]
|
|
: #( c:JOIN_FRAGMENT { out(" "); out(c); } (tableJoin [ c ] )* )
|
|
| #( d:FROM_FRAGMENT { nestedFromFragment(d,parent); } (tableJoin [ d ] )* )
|
|
;
|
|
|
|
booleanOp[ boolean parens ]
|
|
: #(AND booleanExpr[true] { out(" and "); } booleanExpr[true])
|
|
| #(OR { if (parens) out("("); } booleanExpr[false] { out(" or "); } booleanExpr[false] { if (parens) out(")"); })
|
|
| #(NOT { out(" not ("); } booleanExpr[false] { out(")"); } )
|
|
;
|
|
|
|
booleanExpr[ boolean parens ]
|
|
: booleanOp [ parens ]
|
|
| comparisonExpr [ parens ]
|
|
| st:SQL_TOKEN { out(st); } // solely for the purpose of mapping-defined where-fragments
|
|
;
|
|
|
|
comparisonExpr[ boolean parens ]
|
|
: binaryComparisonExpression
|
|
| { if (parens) out("("); } exoticComparisonExpression { if (parens) out(")"); }
|
|
;
|
|
|
|
binaryComparisonExpression
|
|
: #(EQ expr { out("="); } expr)
|
|
| #(NE expr { out("<>"); } expr)
|
|
| #(GT expr { out(">"); } expr)
|
|
| #(GE expr { out(">="); } expr)
|
|
| #(LT expr { out("<"); } expr)
|
|
| #(LE expr { out("<="); } expr)
|
|
;
|
|
|
|
exoticComparisonExpression
|
|
: #(LIKE expr { out(" like "); } expr likeEscape )
|
|
| #(NOT_LIKE expr { out(" not like "); } expr likeEscape)
|
|
| #(BETWEEN expr { out(" between "); } expr { out(" and "); } expr)
|
|
| #(NOT_BETWEEN expr { out(" not between "); } expr { out(" and "); } expr)
|
|
| #(IN expr { out(" in"); } inList )
|
|
| #(NOT_IN expr { out(" not in "); } inList )
|
|
| #(EXISTS { optionalSpace(); out("exists "); } quantified )
|
|
| #(IS_NULL expr) { out(" is null"); }
|
|
| #(IS_NOT_NULL expr) { out(" is not null"); }
|
|
;
|
|
|
|
likeEscape
|
|
: ( #(ESCAPE { out(" escape "); } expr) )?
|
|
;
|
|
|
|
inList
|
|
: #(IN_LIST { out(" "); } ( parenSelect | simpleExprList ) )
|
|
;
|
|
|
|
simpleExprList
|
|
: { out("("); } (e:simpleExpr { separator(e," , "); } )* { out(")"); }
|
|
;
|
|
|
|
// A simple expression, or a sub-select with parens around it.
|
|
expr
|
|
: simpleExpr
|
|
| #( VECTOR_EXPR { out("("); } (e:expr { separator(e," , "); } )* { out(")"); } )
|
|
| parenSelect
|
|
| #(ANY { out("any "); } quantified )
|
|
| #(ALL { out("all "); } quantified )
|
|
| #(SOME { out("some "); } quantified )
|
|
;
|
|
|
|
quantified
|
|
: { out("("); } ( sqlToken | selectStatement ) { out(")"); }
|
|
;
|
|
|
|
parenSelect
|
|
: { out("("); } selectStatement { out(")"); }
|
|
;
|
|
|
|
simpleExpr
|
|
: c:constant { out(c); }
|
|
| NULL { out("null"); }
|
|
| addrExpr
|
|
| sqlToken
|
|
| aggregate
|
|
| methodCall
|
|
| count
|
|
| parameter
|
|
| arithmeticExpr
|
|
;
|
|
|
|
constant
|
|
: NUM_DOUBLE
|
|
| NUM_FLOAT
|
|
| NUM_INT
|
|
| NUM_LONG
|
|
| QUOTED_STRING
|
|
| CONSTANT
|
|
| JAVA_CONSTANT
|
|
| TRUE
|
|
| FALSE
|
|
| IDENT
|
|
;
|
|
|
|
arithmeticExpr
|
|
: additiveExpr
|
|
| multiplicativeExpr
|
|
// | #(CONCAT { out("("); } expr ( { out("||"); } expr )+ { out(")"); } )
|
|
| #(UNARY_MINUS { out("-"); } expr)
|
|
| caseExpr
|
|
;
|
|
|
|
additiveExpr
|
|
: #(PLUS expr { out("+"); } expr)
|
|
| #(MINUS expr { out("-"); } nestedExprAfterMinusDiv)
|
|
;
|
|
|
|
multiplicativeExpr
|
|
: #(STAR nestedExpr { out("*"); } nestedExpr)
|
|
| #(DIV nestedExpr { out("/"); } nestedExprAfterMinusDiv)
|
|
;
|
|
|
|
nestedExpr
|
|
// Generate parens around nested additive expressions, use a syntactic predicate to avoid conflicts with 'expr'.
|
|
: (additiveExpr) => { out("("); } additiveExpr { out(")"); }
|
|
| expr
|
|
;
|
|
|
|
nestedExprAfterMinusDiv
|
|
// Generate parens around nested arithmetic expressions, use a syntactic predicate to avoid conflicts with 'expr'.
|
|
: (arithmeticExpr) => { out("("); } arithmeticExpr { out(")"); }
|
|
| expr
|
|
;
|
|
|
|
caseExpr
|
|
: #(CASE { out("case"); }
|
|
( #(WHEN { out( " when "); } booleanExpr[false] { out(" then "); } expr) )+
|
|
( #(ELSE { out(" else "); } expr) )?
|
|
{ out(" end"); } )
|
|
| #(CASE2 { out("case "); } expr
|
|
( #(WHEN { out( " when "); } expr { out(" then "); } expr) )+
|
|
( #(ELSE { out(" else "); } expr) )?
|
|
{ out(" end"); } )
|
|
;
|
|
|
|
aggregate
|
|
: #(a:AGGREGATE { out(a); out("("); } expr { out(")"); } )
|
|
;
|
|
|
|
|
|
methodCall
|
|
: #(m:METHOD_CALL i:METHOD_NAME { beginFunctionTemplate(m,i); }
|
|
( #(EXPR_LIST (arguments)? ) )?
|
|
{ endFunctionTemplate(m); } )
|
|
;
|
|
|
|
arguments
|
|
: expr ( { commaBetweenParameters(", "); } expr )*
|
|
;
|
|
|
|
parameter
|
|
: n:NAMED_PARAM { out(n); }
|
|
| p:PARAM { out(p); }
|
|
;
|
|
|
|
addrExpr
|
|
: #(r:DOT . .) { out(r); }
|
|
| i:ALIAS_REF { out(i); }
|
|
| j:INDEX_OP { out(j); }
|
|
;
|
|
|
|
sqlToken
|
|
: t:SQL_TOKEN { out(t); }
|
|
;
|
|
|