KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hibernate > hql > ast > HqlSqlWalker


1 // $Id: HqlSqlWalker.java,v 1.105 2005/07/20 19:35:21 steveebersole Exp $
2
package org.hibernate.hql.ast;
3
4 import java.io.Serializable JavaDoc;
5 import java.util.ArrayList JavaDoc;
6 import java.util.Date JavaDoc;
7 import java.util.HashMap JavaDoc;
8 import java.util.HashSet JavaDoc;
9 import java.util.Iterator JavaDoc;
10 import java.util.List JavaDoc;
11 import java.util.Map JavaDoc;
12 import java.util.Set JavaDoc;
13
14 import org.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.hibernate.QueryException;
17 import org.hibernate.engine.JoinSequence;
18 import org.hibernate.engine.ParameterBinder;
19 import org.hibernate.engine.SessionFactoryImplementor;
20 import org.hibernate.hql.QueryTranslator;
21 import org.hibernate.hql.antlr.HqlSqlBaseWalker;
22 import org.hibernate.hql.antlr.HqlSqlTokenTypes;
23 import org.hibernate.hql.antlr.HqlTokenTypes;
24 import org.hibernate.hql.antlr.SqlTokenTypes;
25 import org.hibernate.hql.ast.tree.AssignmentSpecification;
26 import org.hibernate.hql.ast.tree.CollectionFunction;
27 import org.hibernate.hql.ast.tree.ConstructorNode;
28 import org.hibernate.hql.ast.tree.DeleteStatement;
29 import org.hibernate.hql.ast.tree.DotNode;
30 import org.hibernate.hql.ast.tree.FromClause;
31 import org.hibernate.hql.ast.tree.FromElement;
32 import org.hibernate.hql.ast.tree.FromReferenceNode;
33 import org.hibernate.hql.ast.tree.IdentNode;
34 import org.hibernate.hql.ast.tree.IndexNode;
35 import org.hibernate.hql.ast.tree.InsertStatement;
36 import org.hibernate.hql.ast.tree.IntoClause;
37 import org.hibernate.hql.ast.tree.MethodNode;
38 import org.hibernate.hql.ast.tree.ParameterNode;
39 import org.hibernate.hql.ast.tree.QueryNode;
40 import org.hibernate.hql.ast.tree.ResolvableNode;
41 import org.hibernate.hql.ast.tree.RestrictableStatement;
42 import org.hibernate.hql.ast.tree.SelectClause;
43 import org.hibernate.hql.ast.tree.SelectExpression;
44 import org.hibernate.hql.ast.tree.UpdateStatement;
45 import org.hibernate.hql.ast.util.ASTPrinter;
46 import org.hibernate.hql.ast.util.ASTUtil;
47 import org.hibernate.hql.ast.util.AliasGenerator;
48 import org.hibernate.hql.ast.util.JoinProcessor;
49 import org.hibernate.hql.ast.util.LiteralProcessor;
50 import org.hibernate.hql.ast.util.SessionFactoryHelper;
51 import org.hibernate.hql.ast.util.SyntheticAndFactory;
52 import org.hibernate.id.IdentifierGenerator;
53 import org.hibernate.id.PostInsertIdentifierGenerator;
54 import org.hibernate.id.SequenceGenerator;
55 import org.hibernate.param.NamedParameterSpecification;
56 import org.hibernate.param.ParameterSpecification;
57 import org.hibernate.param.PositionalParameterSpecification;
58 import org.hibernate.param.VersionTypeSeedParameterSpecification;
59 import org.hibernate.persister.collection.QueryableCollection;
60 import org.hibernate.persister.entity.Queryable;
61 import org.hibernate.sql.JoinFragment;
62 import org.hibernate.type.AssociationType;
63 import org.hibernate.type.Type;
64 import org.hibernate.type.VersionType;
65 import org.hibernate.usertype.UserVersionType;
66 import org.hibernate.util.ArrayHelper;
67
68 import antlr.ASTFactory;
69 import antlr.RecognitionException;
70 import antlr.SemanticException;
71 import antlr.collections.AST;
72
73 /**
74  * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
75  * <ul>
76  * <li>Isolates the Hibernate API-specific code from the ANTLR generated code.</li>
77  * <li>Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses,
78  * taking into account the joins and projections that are implied by the mappings (persister/queryable).</li>
79  * <li>Uses SqlASTFactory to create customized AST nodes.</li>
80  * </ul>
81  *
82  * @see SqlASTFactory
83  */

84 public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
85     private static Log log = LogFactory.getLog( HqlSqlWalker.class );
86
87     /**
88      * A delegate that handles the Hiberanate meta data model.
89      */

90     private SessionFactoryHelper sessionFactoryHelper;
91
92     /**
93      * A delegate that handles literal constants.
94      */

95     private LiteralProcessor literalProcessor;
96
97     /**
98      * Handles parser errors.
99      */

100     private ParseErrorHandler parseErrorHandler;
101
102     /**
103      * The current context.
104      */

105     private FromClause currentFromClause = null;
106
107     /**
108      * The top-level SelectClause.
109      */

110     private SelectClause selectClause;
111
112     /**
113      * Generates alias names for tables.
114      */

115     private AliasGenerator aliasGenerator = new AliasGenerator();
116
117     /**
118      * The set of unique query spaces (a.k.a. table names).
119      */

120     private Set JavaDoc querySpaces = new HashSet JavaDoc();
121
122     /**
123      * A (string->string) map is used to substitute function names and literals.
124      */

125     private Map JavaDoc tokenReplacements;
126
127     private QueryTranslatorImpl queryTranslatorImpl;
128
129     /**
130      * The number of parameters encountered so far.
131      */

132     private int parameterCount;
133
134     /**
135      * A map of lists which associates named and numbered parameters to a list of occurrences in the query.
136      */

137     private Map JavaDoc namedParameters = new HashMap JavaDoc();
138
139     /**
140      * The filter collection role, or null if this isn't a filter compilation.
141      */

142     private String JavaDoc filterCollectionRole;
143
144     /**
145      * The parser that performed phase 1 - parse the HQL into an HQL tree.
146      */

147     private HqlParser hqlParser;
148
149     private ASTPrinter printer;
150
151     /**
152      * The join type for any implied joins.
153      */

154     private int impliedJoinType;
155
156     private ArrayList JavaDoc parameters = new ArrayList JavaDoc();
157     private int numberOfParametersInSetClause;
158     private int positionalParameterCount;
159
160     private ArrayList JavaDoc assignmentSpecifications = new ArrayList JavaDoc();
161
162     /**
163      * Create a new tree transformer.
164      *
165      * @param qti Back pointer to the query translator implementation that is using this tree transform.
166      * @param sfi The session factory implementor where the Hibernate mappings can be found.
167      * @param parser
168      * @param tokenReplacements Registers the token replacement map with the walker. This map will
169      * be used to substitute function names and constants.
170      * @param collectionRole the role name of the collection used as the basis for the filter, NULL if this
171      */

172     public HqlSqlWalker(QueryTranslatorImpl qti,
173                         SessionFactoryImplementor sfi,
174                         HqlParser parser,
175                         Map JavaDoc tokenReplacements,
176                         String JavaDoc collectionRole) {
177         setASTFactory( new SqlASTFactory( this ) );
178         // Initialize the error handling delegate.
179
this.parseErrorHandler = new ErrorCounter();
180         this.queryTranslatorImpl = qti;
181         this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
182         this.literalProcessor = new LiteralProcessor( this );
183         this.tokenReplacements = tokenReplacements;
184         this.filterCollectionRole = collectionRole;
185         this.hqlParser = parser;
186         this.printer = new ASTPrinter( SqlTokenTypes.class );
187     }
188
189
190     protected void prepareFromClauseInputTree(AST fromClauseInput) {
191         // Handle fiter compilation.
192
// IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
193
if ( isFilter() && !isSubQuery() ) {
194             QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
195             Type collectionElementType = persister.getElementType();
196             if ( !collectionElementType.isEntityType() ) {
197                 throw new QueryException( "collection of values in filter: this" );
198             }
199
200             String JavaDoc collectionElementEntityName = persister.getElementPersister().getEntityName();
201             ASTFactory inputAstFactory = hqlParser.getASTFactory();
202             AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
203             ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
204             fromClauseInput.addChild( fromElement );
205             // Show the modified AST.
206
if ( log.isDebugEnabled() ) {
207                 log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
208             }
209             queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
210         }
211     }
212
213     private boolean isFilter() {
214         return filterCollectionRole != null;
215     }
216
217     public SessionFactoryHelper getSessionFactoryHelper() {
218         return sessionFactoryHelper;
219     }
220
221     public Map JavaDoc getTokenReplacements() {
222         return tokenReplacements;
223     }
224
225     public AliasGenerator getAliasGenerator() {
226         return aliasGenerator;
227     }
228
229     public FromClause getCurrentFromClause() {
230         return currentFromClause;
231     }
232
233     public ParseErrorHandler getParseErrorHandler() {
234         return parseErrorHandler;
235     }
236
237     public void reportError(RecognitionException e) {
238         parseErrorHandler.reportError( e ); // Use the delegate.
239
}
240
241     public void reportError(String JavaDoc s) {
242         parseErrorHandler.reportError( s ); // Use the delegate.
243
}
244
245     public void reportWarning(String JavaDoc s) {
246         parseErrorHandler.reportWarning( s );
247     }
248
249     /**
250      * Returns the set of unique query spaces (a.k.a.
251      * table names) that occurred in the query.
252      *
253      * @return A set of table names (Strings).
254      */

255     public Set JavaDoc getQuerySpaces() {
256         return querySpaces;
257     }
258
259     protected AST createFromElement(String JavaDoc path, AST alias, AST propertyFetch) throws SemanticException {
260         FromElement fromElement = currentFromClause.addFromElement( path, alias );
261         fromElement.setAllPropertyFetch(propertyFetch!=null);
262         return fromElement;
263     }
264
265     protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
266         FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
267         FromClause fromClause = fromElement.getFromClause();
268         QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
269         // Get the names of the columns used to link between the collection
270
// owner and the collection elements.
271
String JavaDoc[] keyColumnNames = persister.getKeyColumnNames();
272         String JavaDoc fkTableAlias = persister.isOneToMany()
273                 ? fromElement.getTableAlias()
274                 : fromClause.getAliasGenerator().createName( filterCollectionRole );
275         JoinSequence join = sessionFactoryHelper.createJoinSequence();
276         join.setRoot( persister, fkTableAlias );
277         if ( !persister.isOneToMany() ) {
278             join.addJoin( ( AssociationType ) persister.getElementType(),
279                     fromElement.getTableAlias(),
280                     JoinFragment.INNER_JOIN,
281                     persister.getElementColumnNames( fkTableAlias ) );
282         }
283         join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
284         fromElement.setJoinSequence( join );
285         fromElement.setFilter( true );
286         if ( log.isDebugEnabled() ) {
287             log.debug( "createFromFilterElement() : processed filter FROM element." );
288         }
289         return fromElement;
290     }
291
292     protected void createFromJoinElement(
293             AST path,
294             AST alias,
295             int joinType,
296             AST fetchNode,
297             AST propertyFetch,
298             AST with) throws SemanticException {
299         boolean fetch = ( fetchNode != null ) ? true : false;
300         // The path AST should be a DotNode, and it should have been evaluated already.
301
if ( path.getType() != SqlTokenTypes.DOT ) {
302             throw new SemanticException( "Path expected for join!" );
303         }
304         DotNode dot = ( DotNode ) path;
305         int hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
306         dot.setJoinType( hibernateJoinType ); // Tell the dot node about the join type.
307
dot.setFetch( fetch );
308         // Generate an explicit join for the root dot node. The implied joins will be collected and passed up
309
// to the root dot node.
310
dot.resolve( true, false, alias == null ? null : alias.getText() );
311         FromElement fromElement = dot.getImpliedJoin();
312         fromElement.setAllPropertyFetch(propertyFetch!=null);
313
314         if ( with != null ) {
315             if ( fetch ) {
316                 throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
317             }
318             handleWithFragment( fromElement, with );
319         }
320
321         if ( log.isDebugEnabled() ) {
322             log.debug( "createFromJoinElement() : " + getASTPrinter().showAsString( fromElement, "-- join tree --" ) );
323         }
324     }
325
326     private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException
327     {
328         try {
329             withClause( hqlWithNode );
330             AST hqlSqlWithNode = returnAST;
331             SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() );
332             sql.whereExpr( hqlSqlWithNode.getFirstChild() );
333             fromElement.setAdHocOnClauseFragment( "(" + sql.getSQL() + ")" );
334
335         } catch ( Exception JavaDoc e) {
336             throw new SemanticException( e.getMessage() );
337         }
338     }
339
340     /**
341      * Sets the current 'FROM' context.
342      *
343      * @param fromNode The new 'FROM' context.
344      * @param inputFromNode The from node from the input AST.
345      */

346     protected void pushFromClause(AST fromNode, AST inputFromNode) {
347         FromClause newFromClause = ( FromClause ) fromNode;
348         newFromClause.setParentFromClause( currentFromClause );
349         currentFromClause = newFromClause;
350     }
351
352     /**
353      * Returns to the previous 'FROM' context.
354      */

355     private void popFromClause() {
356         currentFromClause = currentFromClause.getParentFromClause();
357     }
358
359     protected void lookupAlias(AST aliasRef)
360             throws SemanticException {
361         FromElement alias = currentFromClause.getFromElement( aliasRef.getText() );
362         FromReferenceNode aliasRefNode = ( FromReferenceNode ) aliasRef;
363         aliasRefNode.setFromElement( alias );
364     }
365
366     protected void setImpliedJoinType(int joinType) {
367         impliedJoinType = JoinProcessor.toHibernateJoinType( joinType );
368     }
369
370     public int getImpliedJoinType() {
371         return impliedJoinType;
372     }
373
374     protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException {
375         DotNode dotNode = ( DotNode ) dot;
376         FromReferenceNode lhs = dotNode.getLhs();
377         AST rhs = lhs.getNextSibling();
378         switch ( rhs.getType() ) {
379             case SqlTokenTypes.ELEMENTS:
380             case SqlTokenTypes.INDICES:
381                 if ( log.isDebugEnabled() ) {
382                     log.debug( "lookupProperty() " + dotNode.getPath() + " => " + rhs.getText() + "(" + lhs.getPath() + ")" );
383                 }
384                 CollectionFunction f = ( CollectionFunction ) rhs;
385                 // Re-arrange the tree so that the collection function is the root and the lhs is the path.
386
f.setFirstChild( lhs );
387                 lhs.setNextSibling( null );
388                 dotNode.setFirstChild( f );
389                 resolve( lhs ); // Don't forget to resolve the argument!
390
f.resolve( inSelect ); // Resolve the collection function now.
391
return f;
392             default:
393                 // Resolve everything up to this dot, but don't resolve the placeholders yet.
394
dotNode.resolveFirstChild();
395                 return dotNode;
396         }
397     }
398
399     protected boolean isNonQualifiedPropertyRef(AST ident) {
400         final String JavaDoc identText = ident.getText();
401         if ( currentFromClause.isFromElementAlias( identText ) ) {
402             return false;
403         }
404
405         List JavaDoc fromElements = currentFromClause.getExplicitFromElements();
406         if ( fromElements.size() == 1 ) {
407             final FromElement fromElement = ( FromElement ) fromElements.get( 0 );
408             try {
409                 log.trace( "attempting to resolve property [" + identText + "] as a non-qualified ref" );
410                 return fromElement.getPropertyMapping( identText ).toType( identText ) != null;
411             }
412             catch( QueryException e ) {
413                 // Should mean that no such property was found
414
}
415         }
416
417         return false;
418     }
419
420     protected AST lookupNonQualifiedProperty(AST property) throws SemanticException {
421         final FromElement fromElement = ( FromElement ) currentFromClause.getExplicitFromElements().get( 0 );
422         AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement );
423         return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT );
424     }
425
426     private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) {
427         AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" );
428         // TODO : better way?!?
429
( ( DotNode ) dot ).setPropertyPath( ( ( FromReferenceNode ) property ).getPath() );
430
431         IdentNode syntheticAlias = ( IdentNode ) getASTFactory().create( IDENT, "{synthetic-alias}" );
432         syntheticAlias.setFromElement( fromElement );
433         syntheticAlias.setResolved();
434
435         dot.setFirstChild( syntheticAlias );
436         dot.addChild( property );
437
438         return dot;
439     }
440
441     protected void processQuery(AST select, AST query) throws SemanticException {
442         if ( log.isDebugEnabled() ) {
443             log.debug( "processQuery() : " + query.toStringTree() );
444         }
445
446         try {
447             QueryNode qn = ( QueryNode ) query;
448
449             // Was there an explicit select expression?
450
boolean explicitSelect = select != null && select.getNumberOfChildren() > 0;
451
452             if ( !explicitSelect ) {
453                 // No explicit select expression; render the id and properties
454
// projection lists for every persister in the from clause into
455
// a single 'token node'.
456
//TODO: the only reason we need this stuff now is collection filters,
457
// we should get rid of derived select clause completely!
458
createSelectClauseFromFromClause( qn );
459             }
460             else {
461                 // Use the explicitly declared select expression; determine the
462
// return types indicated by each select token
463
useSelectClause( select );
464             }
465
466             // After that, process the JOINs.
467
// Invoke a delegate to do the work, as this is farily complex.
468
JoinProcessor joinProcessor = new JoinProcessor( astFactory, queryTranslatorImpl );
469             joinProcessor.processJoins( qn );
470
471             // Attach any mapping-defined "ORDER BY" fragments
472
Iterator JavaDoc itr = qn.getFromClause().getProjectionList().iterator();
473             while ( itr.hasNext() ) {
474                 final FromElement fromElement = ( FromElement ) itr.next();
475 // if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) {
476
if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) {
477                     // Does the collection referenced by this FromElement
478
// specify an order-by attribute? If so, attach it to
479
// the query's order-by
480
if ( fromElement.getQueryableCollection().hasOrdering() ) {
481                         String JavaDoc orderByFragment = fromElement
482                                 .getQueryableCollection()
483                                 .getSQLOrderByString( fromElement.getCollectionTableAlias() );
484                         qn.getOrderByClause().addOrderFragment( orderByFragment );
485                     }
486                 }
487             }
488         }
489         finally {
490             popFromClause();
491         }
492     }
493
494     protected void postProcessDML(RestrictableStatement statement) throws SemanticException {
495         statement.getFromClause().resolve();
496
497         FromElement fromElement = ( FromElement ) statement.getFromClause().getFromElements().get( 0 );
498         Queryable persister = fromElement.getQueryable();
499         // Make #@%$^#^&# sure no alias is applied to the table name
500
fromElement.setText( persister.getTableName() );
501
502         // append any filter fragments; the EMPTY_MAP is used under the assumption that
503
// currently enabled filters should not affect this process
504
if ( persister.getDiscriminatorType() != null ) {
505             new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment(
506                     statement,
507                     persister,
508                     java.util.Collections.EMPTY_MAP,
509                     fromElement.getTableAlias()
510             );
511         }
512
513     }
514
515     protected void postProcessUpdate(AST update) throws SemanticException {
516         UpdateStatement updateStatement = ( UpdateStatement ) update;
517
518         postProcessDML( updateStatement );
519     }
520
521     protected void postProcessDelete(AST delete) throws SemanticException {
522         postProcessDML( ( DeleteStatement ) delete );
523     }
524
525     public static boolean supportsIdGenWithBulkInsertion(IdentifierGenerator generator) {
526         return SequenceGenerator.class.isAssignableFrom( generator.getClass() )
527                 || PostInsertIdentifierGenerator.class.isAssignableFrom( generator.getClass() );
528     }
529
530     protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
531         InsertStatement insertStatement = ( InsertStatement ) insert;
532         insertStatement.validate();
533
534         SelectClause selectClause = insertStatement.getSelectClause();
535         Queryable persister = insertStatement.getIntoClause().getQueryable();
536
537         if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
538             // We need to generate ids as part of this bulk insert.
539
//
540
// Note that this is only supported for sequence-style generators and
541
// post-insert-style generators; basically, only in-db generators
542
IdentifierGenerator generator = persister.getIdentifierGenerator();
543             if ( !supportsIdGenWithBulkInsertion( generator ) ) {
544                 throw new QueryException( "can only generate ids as part of bulk insert with either sequence or post-insert style generators" );
545             }
546
547             AST idSelectExprNode = null;
548
549             if ( SequenceGenerator.class.isAssignableFrom( generator.getClass() ) ) {
550                 String JavaDoc seqName = ( String JavaDoc ) ( ( SequenceGenerator ) generator ).generatorKey();
551                 String JavaDoc nextval = sessionFactoryHelper.getFactory().getDialect().getSelectSequenceNextValString( seqName );
552                 idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, nextval );
553             }
554             else {
555                 String JavaDoc idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString();
556                 if ( idInsertString != null ) {
557                     idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString );
558                 }
559             }
560
561             if ( idSelectExprNode != null ) {
562                 AST currentFirstSelectExprNode = selectClause.getFirstChild();
563                 selectClause.setFirstChild( idSelectExprNode );
564                 idSelectExprNode.setNextSibling( currentFirstSelectExprNode );
565
566                 insertStatement.getIntoClause().prependIdColumnSpec();
567             }
568         }
569
570         if ( persister.isVersioned() && !insertStatement.getIntoClause().isExplicitVersionInsertion() ) {
571             // We need to re-seed the version value as part of this bulk insert
572
VersionType versionType = persister.getVersionType();
573
574             AST versionValue = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
575             ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
576             ( ( ParameterNode ) versionValue ).setHqlParameterSpecification( paramSpec );
577             parameters.add( 0, paramSpec );
578
579             AST currentFirstSelectExprNode = selectClause.getFirstChild();
580             selectClause.setFirstChild( versionValue );
581             versionValue.setNextSibling( currentFirstSelectExprNode );
582
583             insertStatement.getIntoClause().prependVersionColumnSpec();
584         }
585
586         if ( insertStatement.getIntoClause().isDiscriminated() ) {
587             String JavaDoc sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue();
588             AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue );
589             insertStatement.getSelectClause().addChild( discrimValue );
590         }
591
592     }
593
594     private void useSelectClause(AST select) throws SemanticException {
595         selectClause = ( SelectClause ) select;
596         selectClause.initializeExplicitSelectClause( currentFromClause );
597     }
598
599     private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException {
600         AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" );
601         AST sibling = qn.getFromClause();
602         qn.setFirstChild( select );
603         select.setNextSibling( sibling );
604         selectClause = ( SelectClause ) select;
605         selectClause.initializeDerivedSelectClause( currentFromClause );
606         if ( log.isDebugEnabled() ) {
607             log.debug( "Derived SELECT clause created." );
608         }
609     }
610
611     protected void resolve(AST node) throws SemanticException {
612         if ( node != null ) {
613             // This is called when it's time to fully resolve a path expression.
614
ResolvableNode r = ( ResolvableNode ) node;
615             if ( isInFunctionCall() ) {
616                 r.resolveInFunctionCall( false, true );
617             }
618             else {
619                 r.resolve( false, true ); // Generate implicit joins, only if necessary.
620
}
621         }
622     }
623
624     protected void resolveSelectExpression(AST node) throws SemanticException {
625         // This is called when it's time to fully resolve a path expression.
626
int type = node.getType();
627         switch ( type ) {
628             case DOT:
629                 DotNode dot = ( DotNode ) node;
630                 dot.resolveSelectExpression();
631                 break;
632             case ALIAS_REF:
633                 // Notify the FROM element that it is being referenced by the select.
634
FromReferenceNode aliasRefNode = ( FromReferenceNode ) node;
635                 //aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here?
636
aliasRefNode.resolve( false, false ); //TODO: is it kosher to do it here?
637
FromElement fromElement = aliasRefNode.getFromElement();
638                 if ( fromElement != null ) {
639                     fromElement.setIncludeSubclasses( true );
640                 }
641             default:
642                 break;
643         }
644     }
645
646     protected void beforeSelectClause() throws SemanticException {
647         // Turn off includeSubclasses on all FromElements.
648
FromClause from = getCurrentFromClause();
649         List JavaDoc fromElements = from.getFromElements();
650         for ( Iterator JavaDoc iterator = fromElements.iterator(); iterator.hasNext(); ) {
651             FromElement fromElement = ( FromElement ) iterator.next();
652             fromElement.setIncludeSubclasses( false );
653         }
654     }
655
656     protected void positionalParameter(AST parameter) throws SemanticException {
657         if ( namedParameters.size() > 0 ) {
658             throw new SemanticException( "" );
659         }
660         PositionalParameterSpecification paramSpec = new PositionalParameterSpecification( positionalParameterCount++ );
661         ( ( ParameterNode ) parameter ).setHqlParameterSpecification( paramSpec );
662         parameters.add( paramSpec );
663     }
664
665     protected void namedParameter(AST namedParameter) throws SemanticException {
666         String JavaDoc name = namedParameter.getText();
667         addNamedParameter( name );
668         namedParameter.setText( "?" ); // Named HQL parameters translate into SQL '?' parameters.
669

670         NamedParameterSpecification paramSpec = new NamedParameterSpecification( name );
671         ( ( ParameterNode ) namedParameter ).setHqlParameterSpecification( paramSpec );
672         parameters.add( paramSpec );
673     }
674
675     private void addNamedParameter(String JavaDoc name) {
676         Integer JavaDoc loc = new Integer JavaDoc( parameterCount++ );
677         Object JavaDoc o = namedParameters.get( name );
678         if ( o == null ) {
679             namedParameters.put( name, loc );
680         }
681         else if ( o instanceof Integer JavaDoc ) {
682             ArrayList JavaDoc list = new ArrayList JavaDoc( 4 );
683             list.add( o );
684             list.add( loc );
685             namedParameters.put( name, list );
686         }
687         else {
688             ( ( ArrayList JavaDoc ) o ).add( loc );
689         }
690     }
691
692     protected void processConstant(AST constant) throws SemanticException {
693         literalProcessor.processConstant( constant ); // Use the delegate.
694
}
695
696     protected void processBoolean(AST constant) throws SemanticException {
697         literalProcessor.processBoolean( constant ); // Use the delegate.
698
}
699
700     protected void processIndex(AST indexOp) throws SemanticException {
701         IndexNode indexNode = ( IndexNode ) indexOp;
702         indexNode.resolve( true, true );
703     }
704
705     protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException {
706         MethodNode methodNode = ( MethodNode ) functionCall;
707         methodNode.resolve( inSelect );
708     }
709
710     protected void processConstructor(AST constructor) throws SemanticException {
711         ConstructorNode constructorNode = ( ConstructorNode ) constructor;
712         constructorNode.prepare();
713     }
714
715     protected void setAlias(AST selectExpr, AST ident) {
716         ((SelectExpression) selectExpr).setAlias(ident.getText());
717     }
718
719     protected void setRoot(AST ast) {
720         ( (IdentNode) ast).setRoot();
721     }
722
723     /**
724      * Returns the locations of all occurrences of the named parameter.
725      */

726     public int[] getNamedParameterLocations(String JavaDoc name) throws QueryException {
727         Object JavaDoc o = namedParameters.get( name );
728         if ( o == null ) {
729             QueryException qe = new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
730             qe.setQueryString( queryTranslatorImpl.getQueryString() );
731             throw qe;
732         }
733         if ( o instanceof Integer JavaDoc ) {
734             return new int[]{( ( Integer JavaDoc ) o ).intValue()};
735         }
736         else {
737             return ArrayHelper.toIntArray( ( ArrayList JavaDoc ) o );
738         }
739     }
740
741     public void addQuerySpaces(Serializable JavaDoc[] spaces) {
742         for ( int i = 0; i < spaces.length; i++ ) {
743             querySpaces.add( spaces[i] );
744         }
745     }
746
747     public Type[] getReturnTypes() {
748         return selectClause.getQueryReturnTypes();
749     }
750
751     public String JavaDoc[] getReturnAliases() {
752         return selectClause.getQueryReturnAliases();
753     }
754
755     public SelectClause getSelectClause() {
756         return selectClause;
757     }
758     
759     public FromClause getFinalFromClause() {
760         //TODO: more correct implementation
761
return currentFromClause;
762     }
763
764     public boolean isShallowQuery() {
765         return queryTranslatorImpl.isShallowQuery();
766     }
767
768     public Map JavaDoc getEnabledFilters() {
769         return queryTranslatorImpl.getEnabledFilters();
770     }
771
772     public LiteralProcessor getLiteralProcessor() {
773         return literalProcessor;
774     }
775
776     public ASTPrinter getASTPrinter() {
777         return printer;
778     }
779
780     public ArrayList JavaDoc getParameters() {
781         return parameters;
782     }
783
784     public int getNumberOfParametersInSetClause() {
785         return numberOfParametersInSetClause;
786     }
787
788     protected void evaluateAssignment(AST eq) throws SemanticException {
789         Queryable persister = getCurrentFromClause().getFromElement().getQueryable();
790         evaluateAssignment( eq, persister, -1 );
791     }
792
793     private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) {
794         if ( persister.isMultiTable() ) {
795             // no need to even collect this information if the persister is considered multi-table
796
AssignmentSpecification specification = new AssignmentSpecification( eq, persister );
797             if ( targetIndex >= 0 ) {
798                 assignmentSpecifications.add( targetIndex, specification );
799             }
800             else {
801                 assignmentSpecifications.add( specification );
802             }
803             numberOfParametersInSetClause += specification.getParameters().length;
804         }
805     }
806
807     public ArrayList JavaDoc getAssignmentSpecifications() {
808         return assignmentSpecifications;
809     }
810
811     protected AST createIntoClause(String JavaDoc path, AST propertySpec) throws SemanticException {
812         Queryable persister = ( Queryable ) getSessionFactoryHelper().requireClassPersister( path );
813
814         IntoClause intoClause = ( IntoClause ) getASTFactory().create( INTO, persister.getEntityName() );
815         intoClause.setFirstChild( propertySpec );
816         intoClause.initialize( persister );
817
818         addQuerySpaces( persister.getQuerySpaces() );
819
820         return intoClause;
821     }
822
823     protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException {
824         UpdateStatement updateStatement = ( UpdateStatement ) updateNode;
825         FromClause fromClause = updateStatement.getFromClause();
826         if ( versioned != null ) {
827             // Make sure that the persister is versioned
828
Queryable persister = fromClause.getFromElement().getQueryable();
829             if ( !persister.isVersioned() ) {
830                 throw new SemanticException( "increment option specified for update of non-versioned entity" );
831             }
832
833             VersionType versionType = persister.getVersionType();
834             if ( versionType instanceof UserVersionType ) {
835                 throw new SemanticException( "user-defined version types not supported for increment option" );
836             }
837
838             AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" );
839             AST versionPropertyNode = generateVersionPropertyNode( persister );
840
841             eq.setFirstChild( versionPropertyNode );
842
843             AST versionIncrementNode = null;
844             if ( Date JavaDoc.class.isAssignableFrom( versionType.getReturnedClass() ) ) {
845                 versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
846                 ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
847                 ( ( ParameterNode ) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
848                 parameters.add( 0, paramSpec );
849             }
850             else {
851                 // Not possible to simply re-use the versionPropertyNode here as it causes
852
// OOM errors due to circularity :(
853
versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" );
854                 versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) );
855                 versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) );
856             }
857
858             eq.addChild( versionIncrementNode );
859
860             evaluateAssignment( eq, persister, 0 );
861
862             AST setClause = updateStatement.getSetClause();
863             AST currentFirstSetElement = setClause.getFirstChild();
864             setClause.setFirstChild( eq );
865             eq.setNextSibling( currentFirstSetElement );
866         }
867     }
868
869     private AST generateVersionPropertyNode(Queryable persister) throws SemanticException {
870         String JavaDoc versionPropertyName = persister.getPropertyNames()[ persister.getVersionProperty() ];
871         AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName );
872         AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef );
873         resolve( versionPropertyNode );
874         return versionPropertyNode;
875     }
876
877     public static void panic() {
878         throw new QueryException( "TreeWalker: panic" );
879     }
880 }
881
Popular Tags