Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/hibernate/hql/ast/QueryTranslatorImpl.java


1   // $Id: QueryTranslatorImpl.java,v 1.53 2005/04/26 18:08:02 oneovthafew Exp $
2   package org.hibernate.hql.ast;
3   
4   import antlr.ANTLRException;
5   import antlr.RecognitionException;
6   import antlr.TokenStreamException;
7   import antlr.collections.AST;
8   import org.apache.commons.logging.Log;
9   import org.apache.commons.logging.LogFactory;
10  import org.hibernate.HibernateException;
11  import org.hibernate.MappingException;
12  import org.hibernate.QueryException;
13  import org.hibernate.ScrollableResults;
14  import org.hibernate.engine.QueryParameters;
15  import org.hibernate.engine.SessionFactoryImplementor;
16  import org.hibernate.engine.SessionImplementor;
17  import org.hibernate.hql.FilterTranslator;
18  import org.hibernate.hql.antlr.HqlTokenTypes;
19  import org.hibernate.hql.antlr.SqlTokenTypes;
20  import org.hibernate.loader.hql.QueryLoader;
21  import org.hibernate.type.Type;
22  
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  
29  /**
30   * A QueryTranslator that uses an AST based parser.
31   * <br>User: josh
32   * <br>Date: Dec 31, 2003
33   * <br>Time: 7:50:35 AM
34   *
35   * @author Joshua Davis (pgmjsd@sourceforge.net)
36   */
37  public class QueryTranslatorImpl implements FilterTranslator {
38    private static final Log log = LogFactory.getLog( QueryTranslatorImpl.class );
39    private static final Log AST_LOG = LogFactory.getLog( "org.hibernate.hql.AST" );
40  
41    private String hql;
42    private boolean shallowQuery;
43    private Map tokenReplacements;
44    private Map enabledFilters;
45    private SessionFactoryImplementor factory;
46  
47    private boolean compiled;
48    private QueryLoader queryLoader;
49    private UpdateStatementExecutor updateStatementExecutor;
50  
51    private QueryNode sqlAst;
52    private String sql;
53  
54  
55    /**
56     * Creates a new AST-based query translator.
57     *
58     * @param query          The HQL query string.
59     * @param enabledFilters Any filters currently enabled for the session.
60     * @param factory        The session factory constructing this translator instance.
61     */
62    public QueryTranslatorImpl(String query,
63                   Map enabledFilters,
64                   SessionFactoryImplementor factory) {
65      this.hql = query;
66      this.compiled = false;
67      this.shallowQuery = false;
68      this.enabledFilters = enabledFilters;
69      this.factory = factory;
70    }
71  
72    /**
73     * Compile a "normal" query. This method may be called multiple
74     * times. Subsequent invocations are no-ops.
75     *
76     * @param replacements Defined query substitutions.
77     * @param shallow      Does this represent a shallow (scalar or entity-id) select?
78     * @throws QueryException   There was a problem parsing the query string.
79     * @throws MappingException There was a problem querying defined mappings.
80     */
81    public void compile(Map replacements, boolean shallow)
82        throws QueryException, MappingException {
83      doCompile( replacements, shallow, null );
84    }
85  
86    /**
87     * Compile a filter. This method may be called multiple
88     * times. Subsequent invocations are no-ops.
89     *
90     * @param collectionRole the role name of the collection used as the basis for the filter.
91     * @param replacements   Defined query substitutions.
92     * @param shallow        Does this represent a shallow (scalar or entity-id) select?
93     * @throws QueryException   There was a problem parsing the query string.
94     * @throws MappingException There was a problem querying defined mappings.
95     */
96    public void compile(String collectionRole, Map replacements, boolean shallow)
97        throws QueryException, MappingException {
98      doCompile( replacements, shallow, collectionRole );
99    }
100 
101   /**
102    * Performs both filter and non-filter compiling.
103    *
104    * @param replacements   Defined query substitutions.
105    * @param shallow        Does this represent a shallow (scalar or entity-id) select?
106    * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
107    *                       is not a filter.
108    */
109   private synchronized void doCompile(Map replacements, boolean shallow, String collectionRole) {
110     // If the query is already compiled, skip the compilation.
111     if ( compiled ) {
112       if ( log.isDebugEnabled() ) {
113         log.debug( "compile() : The query is already compiled, skipping..." );
114       }
115       return;
116     }
117 
118     // Remember the parameters for the compilation.
119     this.tokenReplacements = replacements;
120     if ( tokenReplacements == null ) {
121       tokenReplacements = new HashMap();
122     }
123     this.shallowQuery = shallow;
124 
125     try {
126       // PHASE 1 : Parse the HQL into an AST.
127       HqlParser parser = parse( true );
128 
129       // PHASE 2 : Analyze the HQL AST, and produce an SQL AST.
130       HqlSqlWalker w = analyze( parser, collectionRole );
131 
132       sqlAst = ( QueryNode ) w.getAST();
133 
134       // at some point the generate phase needs to be moved out of here,
135       // because a single object-level DML might spawn multiple SQL DML
136       // command executions.
137       //
138       // Possible to just move the sql generation for dml stuff, but for
139       // consistency-sake probably best to just move responsiblity for
140       // the generation phase completely into the delegates
141       // (QueryLoader/UpdateStatementExecutor) themselves.  Also, not sure why
142       // QueryLoader currently even has a dependency on this at all; does
143       // it need it?  Ideally like to see the walker itself given to the delegates directly...
144 
145       // PHASE 3 : Generate the SQL.
146       generate( sqlAst );
147 
148       if ( sqlAst.isDML() ) {
149         updateStatementExecutor = new UpdateStatementExecutor( sql, getWalker(), factory );
150       }
151       else {
152         queryLoader = new QueryLoader( this, factory, w.getSelectClause() );
153       }
154 
155       compiled = true;
156     }
157     catch ( QueryException qe ) {
158       qe.setQueryString( hql );
159       throw qe;
160     }
161     catch ( RecognitionException e ) {
162       throw  new QuerySyntaxError( e, hql );
163     }
164     catch ( ANTLRException e ) {
165       QueryException qe = new QueryException( e.getMessage(), e );
166       qe.setQueryString( hql );
167       throw qe;
168     }
169   }
170 
171   private void generate(AST sqlAst) throws QueryException, RecognitionException {
172     if ( sql == null ) {
173       SqlGenerator gen = new SqlGenerator(factory);
174       gen.statement( sqlAst );
175       sql = gen.getSQL();
176       if ( log.isDebugEnabled() ) {
177         log.debug( "HQL: " + hql );
178         log.debug( "SQL: " + sql );
179       }
180       gen.getParseErrorHandler().throwQueryException();
181     }
182   }
183 
184   private HqlSqlWalker analyze(HqlParser parser, String collectionRole) throws QueryException, RecognitionException {
185     HqlSqlWalker w = new HqlSqlWalker( this, factory, parser, tokenReplacements, collectionRole );
186     AST hqlAst = parser.getAST();
187 
188     // Transform the tree.
189     w.statement( hqlAst );
190 
191     if ( AST_LOG.isDebugEnabled() ) {
192       ASTPrinter printer = new ASTPrinter( SqlTokenTypes.class );
193       AST_LOG.debug( printer.showAsString( w.getAST(), "--- SQL AST ---" ) );
194     }
195 
196     w.getParseErrorHandler().throwQueryException();
197 
198     return w;
199   }
200 
201   private HqlParser parse(boolean filter) throws TokenStreamException, RecognitionException {
202     // Parse the query string into an HQL AST.
203     HqlParser parser = HqlParser.getInstance( hql );
204     parser.setFilter( filter );
205 
206     if ( log.isDebugEnabled() ) {
207       log.debug( "parse() - HQL: " + hql );
208     }
209     parser.statement();
210 
211     AST hqlAst = parser.getAST();
212 
213     showHqlAst( hqlAst );
214 
215     parser.getParseErrorHandler().throwQueryException();
216     return parser;
217   }
218 
219   void showHqlAst(AST hqlAst) {
220     if ( AST_LOG.isDebugEnabled() ) {
221       ASTPrinter printer = new ASTPrinter( HqlTokenTypes.class );
222       printer.setShowClassNames( false ); // The class names aren't interesting in the first tree.
223       AST_LOG.debug( printer.showAsString( hqlAst, "--- HQL AST ---" ) );
224     }
225   }
226 
227   private void errorIfDML() throws HibernateException {
228     if ( sqlAst.isDML() ) {
229       throw new HibernateException( "Not supported for DML operations" );
230     }
231   }
232 
233   private void errorIfSelect() throws HibernateException {
234     if ( !sqlAst.isDML() ) {
235       throw new HibernateException( "Not supported for select queries" );
236     }
237   }
238 
239   private HqlSqlWalker getWalker() {
240     return sqlAst.getWalker();
241   }
242 
243   /**
244    * Types of the return values of an <tt>iterate()</tt> style query.
245    *
246    * @return an array of <tt>Type</tt>s.
247    */
248   public Type[] getReturnTypes() {
249     errorIfDML();
250     return getWalker().getReturnTypes();
251   }
252 
253   public String[] getReturnAliases() {
254     errorIfDML();
255     return getWalker().getReturnAliases();
256   }
257 
258   public String[][] getColumnNames() {
259     errorIfDML();
260     return getWalker().getSelectClause().getColumnNames();
261   }
262 
263   public Set getQuerySpaces() {
264     return getWalker().getQuerySpaces();
265   }
266 
267   public List list(SessionImplementor session, QueryParameters queryParameters)
268       throws HibernateException {
269     // Delegate to the QueryLoader...
270     errorIfDML();
271     return queryLoader.list( session, queryParameters );
272   }
273 
274   /**
275    * Return the query results as an iterator
276    */
277   public Iterator iterate(QueryParameters queryParameters, SessionImplementor session)
278       throws HibernateException {
279     // Delegate to the QueryLoader...
280     errorIfDML();
281     return queryLoader.iterate( queryParameters, session );
282   }
283 
284   /**
285    * Return the query results, as an instance of <tt>ScrollableResults</tt>
286    */
287   public ScrollableResults scroll(QueryParameters queryParameters, SessionImplementor session)
288       throws HibernateException {
289     // Delegate to the QueryLoader...
290     errorIfDML();
291     return queryLoader.scroll( queryParameters, session );
292   }
293 
294   public int executeUpdate(QueryParameters queryParameters, SessionImplementor session)
295       throws HibernateException {
296     errorIfSelect();
297     return updateStatementExecutor.execute( queryParameters, session );
298   }
299 
300   /**
301    * The SQL query string to be called; implemented by all subclasses
302    */
303   public String getSQLString() {
304     return sql;
305   }
306 
307   // -- Package local methods for the QueryLoader delegate --
308 
309   boolean isShallowQuery() {
310     return shallowQuery;
311   }
312 
313   public String getQueryString() {
314     return hql;
315   }
316 
317   public Map getEnabledFilters() {
318     return enabledFilters;
319   }
320 
321   public int[] getNamedParameterLocs(String name) {
322     return getWalker().getNamedParameterLocs( name );
323   }
324 }