1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.hibernate.hql.internal.ast;
26
27 import java.io.Serializable;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Calendar;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import org.hibernate.HibernateException;
40 import org.hibernate.QueryException;
41 import org.hibernate.engine.internal.JoinSequence;
42 import org.hibernate.engine.internal.ParameterBinder;
43 import org.hibernate.engine.spi.SessionFactoryImplementor;
44 import org.hibernate.hql.internal.antlr.HqlSqlBaseWalker;
45 import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes;
46 import org.hibernate.hql.internal.antlr.HqlTokenTypes;
47 import org.hibernate.hql.internal.antlr.SqlTokenTypes;
48 import org.hibernate.hql.internal.ast.tree.AggregateNode;
49 import org.hibernate.hql.internal.ast.tree.AssignmentSpecification;
50 import org.hibernate.hql.internal.ast.tree.CastFunctionNode;
51 import org.hibernate.hql.internal.ast.tree.CollectionFunction;
52 import org.hibernate.hql.internal.ast.tree.ConstructorNode;
53 import org.hibernate.hql.internal.ast.tree.DeleteStatement;
54 import org.hibernate.hql.internal.ast.tree.DotNode;
55 import org.hibernate.hql.internal.ast.tree.FromClause;
56 import org.hibernate.hql.internal.ast.tree.FromElement;
57 import org.hibernate.hql.internal.ast.tree.FromElementFactory;
58 import org.hibernate.hql.internal.ast.tree.FromReferenceNode;
59 import org.hibernate.hql.internal.ast.tree.IdentNode;
60 import org.hibernate.hql.internal.ast.tree.IndexNode;
61 import org.hibernate.hql.internal.ast.tree.InsertStatement;
62 import org.hibernate.hql.internal.ast.tree.IntoClause;
63 import org.hibernate.hql.internal.ast.tree.MethodNode;
64 import org.hibernate.hql.internal.ast.tree.OperatorNode;
65 import org.hibernate.hql.internal.ast.tree.ParameterContainer;
66 import org.hibernate.hql.internal.ast.tree.ParameterNode;
67 import org.hibernate.hql.internal.ast.tree.QueryNode;
68 import org.hibernate.hql.internal.ast.tree.ResolvableNode;
69 import org.hibernate.hql.internal.ast.tree.RestrictableStatement;
70 import org.hibernate.hql.internal.ast.tree.ResultVariableRefNode;
71 import org.hibernate.hql.internal.ast.tree.SelectClause;
72 import org.hibernate.hql.internal.ast.tree.SelectExpression;
73 import org.hibernate.hql.internal.ast.tree.UpdateStatement;
74 import org.hibernate.hql.internal.ast.util.ASTPrinter;
75 import org.hibernate.hql.internal.ast.util.ASTUtil;
76 import org.hibernate.hql.internal.ast.util.AliasGenerator;
77 import org.hibernate.hql.internal.ast.util.JoinProcessor;
78 import org.hibernate.hql.internal.ast.util.LiteralProcessor;
79 import org.hibernate.hql.internal.ast.util.NodeTraverser;
80 import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
81 import org.hibernate.hql.internal.ast.util.SyntheticAndFactory;
82 import org.hibernate.hql.spi.QueryTranslator;
83 import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
84 import org.hibernate.id.IdentifierGenerator;
85 import org.hibernate.internal.CoreMessageLogger;
86 import org.hibernate.internal.util.StringHelper;
87 import org.hibernate.internal.util.collections.ArrayHelper;
88 import org.hibernate.param.CollectionFilterKeyParameterSpecification;
89 import org.hibernate.param.NamedParameterSpecification;
90 import org.hibernate.param.ParameterSpecification;
91 import org.hibernate.param.PositionalParameterSpecification;
92 import org.hibernate.param.VersionTypeSeedParameterSpecification;
93 import org.hibernate.persister.collection.QueryableCollection;
94 import org.hibernate.persister.entity.Queryable;
95 import org.hibernate.sql.JoinType;
96 import org.hibernate.type.AssociationType;
97 import org.hibernate.type.ComponentType;
98 import org.hibernate.type.DbTimestampType;
99 import org.hibernate.type.Type;
100 import org.hibernate.type.VersionType;
101 import org.hibernate.usertype.UserVersionType;
102 import org.jboss.logging.Logger;
103
104 import antlr.ASTFactory;
105 import antlr.RecognitionException;
106 import antlr.SemanticException;
107 import antlr.collections.AST;
108
109
110
111
112
113
114
115
116
117
118
119
120 public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
121
122 private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, HqlSqlWalker.class.getName());
123
124 private final QueryTranslatorImpl queryTranslatorImpl;
125 private final HqlParser hqlParser;
126 private final SessionFactoryHelper sessionFactoryHelper;
127 private final Map tokenReplacements;
128 private final AliasGenerator aliasGenerator = new AliasGenerator();
129 private final LiteralProcessor literalProcessor;
130 private final ParseErrorHandler parseErrorHandler;
131 private final ASTPrinter printer;
132 private final String collectionFilterRole;
133
134 private FromClause currentFromClause = null;
135 private SelectClause selectClause;
136
137
138
139
140
141 private Map<String, SelectExpression> selectExpressionsByResultVariable = new HashMap();
142
143 private Set querySpaces = new HashSet();
144
145 private int parameterCount;
146 private Map namedParameters = new HashMap();
147 private ArrayList parameters = new ArrayList();
148 private int numberOfParametersInSetClause;
149 private int positionalParameterCount;
150
151 private ArrayList assignmentSpecifications = new ArrayList();
152
153 private JoinType impliedJoinType = JoinType.INNER_JOIN;
154
155
156
157
158
159
160
161
162
163
164
165
166 public HqlSqlWalker(
167 QueryTranslatorImpl qti,
168 SessionFactoryImplementor sfi,
169 HqlParser parser,
170 Map tokenReplacements,
171 String collectionRole) {
172 setASTFactory( new SqlASTFactory( this ) );
173
174 this.parseErrorHandler = new ErrorCounter();
175 this.queryTranslatorImpl = qti;
176 this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
177 this.literalProcessor = new LiteralProcessor( this );
178 this.tokenReplacements = tokenReplacements;
179 this.collectionFilterRole = collectionRole;
180 this.hqlParser = parser;
181 this.printer = new ASTPrinter( SqlTokenTypes.class );
182 }
183
184
185
186
187 private int traceDepth = 0;
188
189 @Override
190 public void traceIn(String ruleName, AST tree) {
191 if ( !LOG.isTraceEnabled() ) return;
192 if ( inputState.guessing > 0 ) return;
193 String prefix = StringHelper.repeat( '-', ( traceDepth++ * 2 ) ) + "-> ";
194 String traceText = ruleName + " (" + buildTraceNodeName( tree ) + ")";
195 LOG.trace( prefix + traceText );
196 }
197
198 private String buildTraceNodeName(AST tree) {
199 return tree == null
200 ? "???"
201 : tree.getText() + " [" + printer.getTokenTypeName( tree.getType() ) + "]";
202 }
203
204 @Override
205 public void traceOut(String ruleName, AST tree) {
206 if ( !LOG.isTraceEnabled() ) return;
207 if ( inputState.guessing > 0 ) return;
208 String prefix = "<-" + StringHelper.repeat( '-', ( --traceDepth * 2 ) ) + " ";
209 LOG.trace( prefix + ruleName );
210 }
211
212 @Override
213 protected void prepareFromClauseInputTree(AST fromClauseInput) {
214 if ( !isSubQuery() ) {
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240 if ( isFilter() ) {
241
242
243 QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
244 Type collectionElementType = persister.getElementType();
245 if ( !collectionElementType.isEntityType() ) {
246 throw new QueryException( "collection of values in filter: this" );
247 }
248
249 String collectionElementEntityName = persister.getElementPersister().getEntityName();
250 ASTFactory inputAstFactory = hqlParser.getASTFactory();
251 AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
252 ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
253 fromClauseInput.addChild( fromElement );
254
255 LOG.debug("prepareFromClauseInputTree() : Filter - Added 'this' as a from element...");
256 queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
257
258
259 Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
260 ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
261 CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
262 collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
263 );
264 collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
265 parameters.add( collectionFilterKeyParameterSpec );
266 }
267 }
268 }
269
270 public boolean isFilter() {
271 return collectionFilterRole != null;
272 }
273
274 public String getCollectionFilterRole() {
275 return collectionFilterRole;
276 }
277
278 public SessionFactoryHelper getSessionFactoryHelper() {
279 return sessionFactoryHelper;
280 }
281
282 public Map getTokenReplacements() {
283 return tokenReplacements;
284 }
285
286 public AliasGenerator getAliasGenerator() {
287 return aliasGenerator;
288 }
289
290 public FromClause getCurrentFromClause() {
291 return currentFromClause;
292 }
293
294 public ParseErrorHandler getParseErrorHandler() {
295 return parseErrorHandler;
296 }
297
298 @Override
299 public void reportError(RecognitionException e) {
300 parseErrorHandler.reportError( e );
301 }
302
303 @Override
304 public void reportError(String s) {
305 parseErrorHandler.reportError( s );
306 }
307
308 @Override
309 public void reportWarning(String s) {
310 parseErrorHandler.reportWarning( s );
311 }
312
313
314
315
316
317
318
319 public Set getQuerySpaces() {
320 return querySpaces;
321 }
322
323 @Override
324 protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException {
325 FromElement fromElement = currentFromClause.addFromElement( path, alias );
326 fromElement.setAllPropertyFetch(propertyFetch!=null);
327 return fromElement;
328 }
329
330 @Override
331 protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
332 FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
333 FromClause fromClause = fromElement.getFromClause();
334 QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
335
336
337 String[] keyColumnNames = persister.getKeyColumnNames();
338 String fkTableAlias = persister.isOneToMany()
339 ? fromElement.getTableAlias()
340 : fromClause.getAliasGenerator().createName( collectionFilterRole );
341 JoinSequence join = sessionFactoryHelper.createJoinSequence();
342 join.setRoot( persister, fkTableAlias );
343 if ( !persister.isOneToMany() ) {
344 join.addJoin( ( AssociationType ) persister.getElementType(),
345 fromElement.getTableAlias(),
346 JoinType.INNER_JOIN,
347 persister.getElementColumnNames( fkTableAlias ) );
348 }
349 join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
350 fromElement.setJoinSequence( join );
351 fromElement.setFilter( true );
352 LOG.debug("createFromFilterElement() : processed filter FROM element.");
353 return fromElement;
354 }
355
356 @Override
357 protected void createFromJoinElement(
358 AST path,
359 AST alias,
360 int joinType,
361 AST fetchNode,
362 AST propertyFetch,
363 AST with) throws SemanticException {
364 boolean fetch = fetchNode != null;
365 if ( fetch && isSubQuery() ) {
366 throw new QueryException( "fetch not allowed in subquery from-elements" );
367 }
368
369 if ( path.getType() != SqlTokenTypes.DOT ) {
370 throw new SemanticException( "Path expected for join!" );
371 }
372 DotNode dot = ( DotNode ) path;
373 JoinType hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
374 dot.setJoinType( hibernateJoinType );
375 dot.setFetch( fetch );
376
377
378 dot.resolve( true, false, alias == null ? null : alias.getText() );
379
380 final FromElement fromElement;
381 if ( dot.getDataType() != null && dot.getDataType().isComponentType() ) {
382 FromElementFactory factory = new FromElementFactory(
383 getCurrentFromClause(),
384 dot.getLhs().getFromElement(),
385 dot.getPropertyPath(),
386 alias == null ? null : alias.getText(),
387 null,
388 false
389 );
390 fromElement = factory.createComponentJoin( (ComponentType) dot.getDataType() );
391 }
392 else {
393 fromElement = dot.getImpliedJoin();
394 fromElement.setAllPropertyFetch( propertyFetch != null );
395
396 if ( with != null ) {
397 if ( fetch ) {
398 throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
399 }
400 handleWithFragment( fromElement, with );
401 }
402 }
403
404 if (LOG.isDebugEnabled()) LOG.debugf("createFromJoinElement() : %s",
405 getASTPrinter().showAsString(fromElement, "-- join tree --"));
406 }
407
408 private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException {
409 try {
410 withClause( hqlWithNode );
411 AST hqlSqlWithNode = returnAST;
412 if (LOG.isDebugEnabled()) LOG.debugf("handleWithFragment() : %s",
413 getASTPrinter().showAsString(hqlSqlWithNode, "-- with clause --"));
414 WithClauseVisitor visitor = new WithClauseVisitor( fromElement );
415 NodeTraverser traverser = new NodeTraverser( visitor );
416 traverser.traverseDepthFirst( hqlSqlWithNode );
417
418 String withClauseJoinAlias = visitor.getJoinAlias();
419 if ( withClauseJoinAlias == null ) {
420 withClauseJoinAlias = fromElement.getCollectionTableAlias();
421 }
422 else {
423 FromElement referencedFromElement = visitor.getReferencedFromElement();
424 if ( referencedFromElement != fromElement ) {
425 LOG.warn( "with-clause expressions do not reference the from-clause element to which the with-clause was associated. The query may not work as expected..."
426 + queryTranslatorImpl.getQueryString() );
427 }
428 }
429
430 SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() );
431 sql.whereExpr( hqlSqlWithNode.getFirstChild() );
432
433 fromElement.setWithClauseFragment( withClauseJoinAlias, "(" + sql.getSQL() + ")" );
434 }
435 catch( SemanticException e ) {
436 throw e;
437 }
438 catch( InvalidWithClauseException e ) {
439 throw e;
440 }
441 catch ( Exception e) {
442 throw new SemanticException( e.getMessage() );
443 }
444 }
445
446 private static class WithClauseVisitor implements NodeTraverser.VisitationStrategy {
447 private final FromElement joinFragment;
448 private FromElement referencedFromElement;
449 private String joinAlias;
450
451 public WithClauseVisitor(FromElement fromElement) {
452 this.joinFragment = fromElement;
453 }
454
455 public void visit(AST node) {
456
457
458
459
460
461
462
463
464
465
466 if ( node instanceof DotNode ) {
467 DotNode dotNode = ( DotNode ) node;
468 FromElement fromElement = dotNode.getFromElement();
469 if ( referencedFromElement != null ) {
470 if ( fromElement != referencedFromElement ) {
471 throw new HibernateException( "with-clause referenced two different from-clause elements" );
472 }
473 }
474 else {
475 referencedFromElement = fromElement;
476 joinAlias = extractAppliedAlias( dotNode );
477
478
479
480
481 if ( !joinAlias.equals( referencedFromElement.getTableAlias() ) ) {
482 throw new InvalidWithClauseException( "with clause can only reference columns in the driving table" );
483 }
484 }
485 }
486 else if ( node instanceof ParameterNode ) {
487 applyParameterSpecification( ( ( ParameterNode ) node ).getHqlParameterSpecification() );
488 }
489 else if ( node instanceof ParameterContainer ) {
490 applyParameterSpecifications( ( ParameterContainer ) node );
491 }
492 }
493
494 private void applyParameterSpecifications(ParameterContainer parameterContainer) {
495 if ( parameterContainer.hasEmbeddedParameters() ) {
496 ParameterSpecification[] specs = parameterContainer.getEmbeddedParameters();
497 for ( int i = 0; i < specs.length; i++ ) {
498 applyParameterSpecification( specs[i] );
499 }
500 }
501 }
502
503 private void applyParameterSpecification(ParameterSpecification paramSpec) {
504 joinFragment.addEmbeddedParameter( paramSpec );
505 }
506
507 private String extractAppliedAlias(DotNode dotNode) {
508 return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) );
509 }
510
511 public FromElement getReferencedFromElement() {
512 return referencedFromElement;
513 }
514
515 public String getJoinAlias() {
516 return joinAlias;
517 }
518 }
519
520
521
522
523
524
525
526 @Override
527 protected void pushFromClause(AST fromNode, AST inputFromNode) {
528 FromClause newFromClause = ( FromClause ) fromNode;
529 newFromClause.setParentFromClause( currentFromClause );
530 currentFromClause = newFromClause;
531 }
532
533
534
535
536 private void popFromClause() {
537 currentFromClause = currentFromClause.getParentFromClause();
538 }
539
540 @Override
541 protected void lookupAlias(AST aliasRef)
542 throws SemanticException {
543 FromElement alias = currentFromClause.getFromElement( aliasRef.getText() );
544 FromReferenceNode aliasRefNode = ( FromReferenceNode ) aliasRef;
545 aliasRefNode.setFromElement( alias );
546 }
547
548 @Override
549 protected void setImpliedJoinType(int joinType) {
550 impliedJoinType = JoinProcessor.toHibernateJoinType( joinType );
551 }
552
553 public JoinType getImpliedJoinType() {
554 return impliedJoinType;
555 }
556
557 @Override
558 protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException {
559 DotNode dotNode = ( DotNode ) dot;
560 FromReferenceNode lhs = dotNode.getLhs();
561 AST rhs = lhs.getNextSibling();
562 switch ( rhs.getType() ) {
563 case SqlTokenTypes.ELEMENTS:
564 case SqlTokenTypes.INDICES:
565 if (LOG.isDebugEnabled()) LOG.debugf("lookupProperty() %s => %s(%s)",
566 dotNode.getPath(),
567 rhs.getText(),
568 lhs.getPath());
569 CollectionFunction f = ( CollectionFunction ) rhs;
570
571 f.setFirstChild( lhs );
572 lhs.setNextSibling( null );
573 dotNode.setFirstChild( f );
574 resolve( lhs );
575 f.resolve( inSelect );
576 return f;
577 default:
578
579 dotNode.resolveFirstChild();
580 return dotNode;
581 }
582 }
583
584 @Override
585 protected boolean isNonQualifiedPropertyRef(AST ident) {
586 final String identText = ident.getText();
587 if ( currentFromClause.isFromElementAlias( identText ) ) {
588 return false;
589 }
590
591 List fromElements = currentFromClause.getExplicitFromElements();
592 if ( fromElements.size() == 1 ) {
593 final FromElement fromElement = ( FromElement ) fromElements.get( 0 );
594 try {
595 LOG.tracev( "Attempting to resolve property [{0}] as a non-qualified ref", identText );
596 return fromElement.getPropertyMapping( identText ).toType( identText ) != null;
597 }
598 catch( QueryException e ) {
599
600 }
601 }
602
603 return false;
604 }
605
606 @Override
607 protected AST lookupNonQualifiedProperty(AST property) throws SemanticException {
608 final FromElement fromElement = ( FromElement ) currentFromClause.getExplicitFromElements().get( 0 );
609 AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement );
610 return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT );
611 }
612
613 private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) {
614 AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" );
615
616 ( ( DotNode ) dot ).setPropertyPath( ( ( FromReferenceNode ) property ).getPath() );
617
618 IdentNode syntheticAlias = ( IdentNode ) getASTFactory().create( IDENT, "{synthetic-alias}" );
619 syntheticAlias.setFromElement( fromElement );
620 syntheticAlias.setResolved();
621
622 dot.setFirstChild( syntheticAlias );
623 dot.addChild( property );
624
625 return dot;
626 }
627
628 @Override
629 protected void processQuery(AST select, AST query) throws SemanticException {
630 if ( LOG.isDebugEnabled() ) {
631 LOG.debugf( "processQuery() : %s", query.toStringTree() );
632 }
633
634 try {
635 QueryNode qn = ( QueryNode ) query;
636
637
638 boolean explicitSelect = select != null && select.getNumberOfChildren() > 0;
639
640 if ( !explicitSelect ) {
641
642
643
644
645
646 createSelectClauseFromFromClause( qn );
647 }
648 else {
649
650
651 useSelectClause( select );
652 }
653
654
655
656 JoinProcessor joinProcessor = new JoinProcessor( this );
657 joinProcessor.processJoins( qn );
658
659
660 Iterator itr = qn.getFromClause().getProjectionList().iterator();
661 while ( itr.hasNext() ) {
662 final FromElement fromElement = ( FromElement ) itr.next();
663
664 if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) {
665
666
667
668 if ( fromElement.getQueryableCollection().hasOrdering() ) {
669 String orderByFragment = fromElement
670 .getQueryableCollection()
671 .getSQLOrderByString( fromElement.getCollectionTableAlias() );
672 qn.getOrderByClause().addOrderFragment( orderByFragment );
673 }
674 if ( fromElement.getQueryableCollection().hasManyToManyOrdering() ) {
675 String orderByFragment = fromElement.getQueryableCollection()
676 .getManyToManyOrderByString( fromElement.getTableAlias() );
677 qn.getOrderByClause().addOrderFragment( orderByFragment );
678 }
679 }
680 }
681 }
682 finally {
683 popFromClause();
684 }
685 }
686
687 protected void postProcessDML(RestrictableStatement statement) throws SemanticException {
688 statement.getFromClause().resolve();
689
690 FromElement fromElement = ( FromElement ) statement.getFromClause().getFromElements().get( 0 );
691 Queryable persister = fromElement.getQueryable();
692
693 fromElement.setText( persister.getTableName() );
694
695
696
697
698
699
700
701
702
703
704
705 if ( persister.getDiscriminatorType() != null || ! queryTranslatorImpl.getEnabledFilters().isEmpty() ) {
706 new SyntheticAndFactory( this ).addDiscriminatorWhereFragment(
707 statement,
708 persister,
709 queryTranslatorImpl.getEnabledFilters(),
710 fromElement.getTableAlias()
711 );
712 }
713
714 }
715
716 @Override
717 protected void postProcessUpdate(AST update) throws SemanticException {
718 UpdateStatement updateStatement = ( UpdateStatement ) update;
719
720 postProcessDML( updateStatement );
721 }
722
723 @Override
724 protected void postProcessDelete(AST delete) throws SemanticException {
725 postProcessDML( ( DeleteStatement ) delete );
726 }
727
728 @Override
729 protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
730 InsertStatement insertStatement = ( InsertStatement ) insert;
731 insertStatement.validate();
732
733 SelectClause selectClause = insertStatement.getSelectClause();
734 Queryable persister = insertStatement.getIntoClause().getQueryable();
735
736 if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
737
738
739
740 final IdentifierGenerator generator = persister.getIdentifierGenerator();
741 if ( !BulkInsertionCapableIdentifierGenerator.class.isInstance( generator ) ) {
742 throw new QueryException(
743 "Invalid identifier generator encountered for implicit id handling as part of bulk insertions"
744 );
745 }
746 final BulkInsertionCapableIdentifierGenerator capableGenerator =
747 BulkInsertionCapableIdentifierGenerator.class.cast( generator );
748 if ( ! capableGenerator.supportsBulkInsertionIdentifierGeneration() ) {
749 throw new QueryException(
750 "Identifier generator reported it does not support implicit id handling as part of bulk insertions"
751 );
752 }
753
754 final String fragment = capableGenerator.determineBulkInsertionIdentifierGenerationSelectFragment(
755 sessionFactoryHelper.getFactory().getDialect()
756 );
757 if ( fragment != null ) {
758
759
760
761 AST fragmentNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, fragment );
762
763 AST originalFirstSelectExprNode = selectClause.getFirstChild();
764 selectClause.setFirstChild( fragmentNode );
765 fragmentNode.setNextSibling( originalFirstSelectExprNode );
766
767 insertStatement.getIntoClause().prependIdColumnSpec();
768 }
769 }
770
771 final boolean includeVersionProperty = persister.isVersioned() &&
772 !insertStatement.getIntoClause().isExplicitVersionInsertion() &&
773 persister.isVersionPropertyInsertable();
774 if ( includeVersionProperty ) {
775
776 VersionType versionType = persister.getVersionType();
777 AST versionValueNode = null;
778
779 if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
780 int sqlTypes[] = versionType.sqlTypes( sessionFactoryHelper.getFactory() );
781 if ( sqlTypes == null || sqlTypes.length == 0 ) {
782 throw new IllegalStateException( versionType.getClass() + ".sqlTypes() returns null or empty array" );
783 }
784 if ( sqlTypes.length > 1 ) {
785 throw new IllegalStateException(
786 versionType.getClass() +
787 ".sqlTypes() returns > 1 element; only single-valued versions are allowed."
788 );
789 }
790 versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
791 ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
792 ( ( ParameterNode ) versionValueNode ).setHqlParameterSpecification( paramSpec );
793 parameters.add( 0, paramSpec );
794
795 if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) {
796
797 MethodNode versionMethodNode = ( MethodNode ) getASTFactory().create( HqlSqlTokenTypes.METHOD_CALL, "(" );
798 AST methodIdentNode = getASTFactory().create( HqlSqlTokenTypes.IDENT, "cast" );
799 versionMethodNode.addChild( methodIdentNode );
800 versionMethodNode.initializeMethodNode(methodIdentNode, true );
801 AST castExprListNode = getASTFactory().create( HqlSqlTokenTypes.EXPR_LIST, "exprList" );
802 methodIdentNode.setNextSibling( castExprListNode );
803 castExprListNode.addChild( versionValueNode );
804 versionValueNode.setNextSibling(
805 getASTFactory().create(
806 HqlSqlTokenTypes.IDENT,
807 sessionFactoryHelper.getFactory().getDialect().getTypeName( sqlTypes[0] ) )
808 );
809 processFunction( versionMethodNode, true );
810 versionValueNode = versionMethodNode;
811 }
812 }
813 else {
814 if ( isIntegral( versionType ) ) {
815 try {
816 Object seedValue = versionType.seed( null );
817 versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, seedValue.toString() );
818 }
819 catch( Throwable t ) {
820 throw new QueryException( "could not determine seed value for version on bulk insert [" + versionType + "]" );
821 }
822 }
823 else if ( isDatabaseGeneratedTimestamp( versionType ) ) {
824 String functionName = sessionFactoryHelper.getFactory().getDialect().getCurrentTimestampSQLFunctionName();
825 versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName );
826 }
827 else {
828 throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameters in insert-select statements" );
829 }
830 }
831
832 AST currentFirstSelectExprNode = selectClause.getFirstChild();
833 selectClause.setFirstChild( versionValueNode );
834 versionValueNode.setNextSibling( currentFirstSelectExprNode );
835
836 insertStatement.getIntoClause().prependVersionColumnSpec();
837 }
838
839 if ( insertStatement.getIntoClause().isDiscriminated() ) {
840 String sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue();
841 AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue );
842 insertStatement.getSelectClause().addChild( discrimValue );
843 }
844
845 }
846
847 private boolean isDatabaseGeneratedTimestamp(Type type) {
848
849 return DbTimestampType.class.isAssignableFrom( type.getClass() );
850 }
851
852 private boolean isIntegral(Type type) {
853 return Long.class.isAssignableFrom( type.getReturnedClass() )
854 || Integer.class.isAssignableFrom( type.getReturnedClass() )
855 || long.class.isAssignableFrom( type.getReturnedClass() )
856 || int.class.isAssignableFrom( type.getReturnedClass() );
857 }
858
859 private void useSelectClause(AST select) throws SemanticException {
860 selectClause = ( SelectClause ) select;
861 selectClause.initializeExplicitSelectClause( currentFromClause );
862 }
863
864 private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException {
865 AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" );
866 AST sibling = qn.getFromClause();
867 qn.setFirstChild( select );
868 select.setNextSibling( sibling );
869 selectClause = ( SelectClause ) select;
870 selectClause.initializeDerivedSelectClause( currentFromClause );
871 LOG.debug( "Derived SELECT clause created." );
872 }
873
874 @Override
875 protected void resolve(AST node) throws SemanticException {
876 if ( node != null ) {
877
878 ResolvableNode r = ( ResolvableNode ) node;
879 if ( isInFunctionCall() ) {
880 r.resolveInFunctionCall( false, true );
881 }
882 else {
883 r.resolve( false, true );
884 }
885 }
886 }
887
888 @Override
889 protected void resolveSelectExpression(AST node) throws SemanticException {
890
891 int type = node.getType();
892 switch ( type ) {
893 case DOT: {
894 DotNode dot = ( DotNode ) node;
895 dot.resolveSelectExpression();
896 break;
897 }
898 case ALIAS_REF: {
899
900 FromReferenceNode aliasRefNode = ( FromReferenceNode ) node;
901
902 aliasRefNode.resolve( false, false );
903 FromElement fromElement = aliasRefNode.getFromElement();
904 if ( fromElement != null ) {
905 fromElement.setIncludeSubclasses( true );
906 }
907 break;
908 }
909 default: {
910 break;
911 }
912 }
913 }
914
915 @Override
916 protected void beforeSelectClause() throws SemanticException {
917
918 FromClause from = getCurrentFromClause();
919 List fromElements = from.getFromElements();
920 for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) {
921 FromElement fromElement = ( FromElement ) iterator.next();
922 fromElement.setIncludeSubclasses( false );
923 }
924 }
925
926 @Override
927 protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
928 if ( namedParameters.size() > 0 ) {
929 throw new SemanticException( "cannot define positional parameter after any named parameters have been defined" );
930 }
931 LOG.warnf(
932 "[DEPRECATION] Encountered positional parameter near line %s, column %s. Positional parameter " +
933 "are considered deprecated; use named parameters or JPA-style positional parameters instead.",
934 inputNode.getLine(),
935 inputNode.getColumn()
936 );
937 ParameterNode parameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
938 PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
939 inputNode.getLine(),
940 inputNode.getColumn(),
941 positionalParameterCount++
942 );
943 parameter.setHqlParameterSpecification( paramSpec );
944 parameters.add( paramSpec );
945 return parameter;
946 }
947
948 @Override
949 protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
950 String name = nameNode.getText();
951 trackNamedParameterPositions( name );
952
953
954
955 ParameterNode parameter = ( ParameterNode ) astFactory.create( NAMED_PARAM, name );
956 parameter.setText( "?" );
957
958 NamedParameterSpecification paramSpec = new NamedParameterSpecification(
959 delimiterNode.getLine(),
960 delimiterNode.getColumn(),
961 name
962 );
963 parameter.setHqlParameterSpecification( paramSpec );
964 parameters.add( paramSpec );
965 return parameter;
966 }
967
968 private void trackNamedParameterPositions(String name) {
969 Integer loc = parameterCount++;
970 Object o = namedParameters.get( name );
971 if ( o == null ) {
972 namedParameters.put( name, loc );
973 }
974 else if ( o instanceof Integer ) {
975 ArrayList list = new ArrayList( 4 );
976 list.add( o );
977 list.add( loc );
978 namedParameters.put( name, list );
979 }
980 else {
981 ( ( ArrayList ) o ).add( loc );
982 }
983 }
984
985 @Override
986 protected void processConstant(AST constant) throws SemanticException {
987 literalProcessor.processConstant( constant, true );
988 }
989
990 @Override
991 protected void processBoolean(AST constant) throws SemanticException {
992 literalProcessor.processBoolean( constant );
993 }
994
995 @Override
996 protected void processNumericLiteral(AST literal) {
997 literalProcessor.processNumeric( literal );
998 }
999
1000 @Override
1001 protected void processIndex(AST indexOp) throws SemanticException {
1002 IndexNode indexNode = ( IndexNode ) indexOp;
1003 indexNode.resolve( true, true );
1004 }
1005
1006 @Override
1007 protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException {
1008 MethodNode methodNode = ( MethodNode ) functionCall;
1009 methodNode.resolve( inSelect );
1010 }
1011
1012 @Override
1013 protected void processCastFunction(AST castFunctionCall, boolean inSelect) throws SemanticException {
1014 CastFunctionNode castFunctionNode = (CastFunctionNode) castFunctionCall;
1015 castFunctionNode.resolve( inSelect );
1016 }
1017
1018 @Override
1019 protected void processAggregation(AST node, boolean inSelect) throws SemanticException {
1020 AggregateNode aggregateNode = (AggregateNode) node;
1021 aggregateNode.resolve();
1022 }
1023
1024 @Override
1025 protected void processConstructor(AST constructor) throws SemanticException {
1026 ConstructorNode constructorNode = ( ConstructorNode ) constructor;
1027 constructorNode.prepare();
1028 }
1029
1030 @Override
1031 protected void setAlias(AST selectExpr, AST ident) {
1032 ((SelectExpression) selectExpr).setAlias(ident.getText());
1033
1034
1035 if ( ! isSubQuery() ) {
1036 selectExpressionsByResultVariable.put( ident.getText(), ( SelectExpression ) selectExpr );
1037 }
1038 }
1039
1040 @Override
1041 protected boolean isOrderExpressionResultVariableRef(AST orderExpressionNode) throws SemanticException {
1042
1043
1044 if ( ! isSubQuery() &&
1045 orderExpressionNode.getType() == IDENT &&
1046 selectExpressionsByResultVariable.containsKey( orderExpressionNode.getText() ) ) {
1047 return true;
1048 }
1049 return false;
1050 }
1051
1052 @Override
1053 protected void handleResultVariableRef(AST resultVariableRef) throws SemanticException {
1054 if ( isSubQuery() ) {
1055 throw new SemanticException(
1056 "References to result variables in subqueries are not supported."
1057 );
1058 }
1059 ( ( ResultVariableRefNode ) resultVariableRef ).setSelectExpression(
1060 selectExpressionsByResultVariable.get( resultVariableRef.getText() )
1061 );
1062 }
1063
1064
1065
1066
1067 public int[] getNamedParameterLocations(String name) throws QueryException {
1068 Object o = namedParameters.get( name );
1069 if ( o == null ) {
1070 QueryException qe = new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
1071 qe.setQueryString( queryTranslatorImpl.getQueryString() );
1072 throw qe;
1073 }
1074 if ( o instanceof Integer ) {
1075 return new int[]{( ( Integer ) o ).intValue()};
1076 }
1077 else {
1078 return ArrayHelper.toIntArray( (ArrayList) o );
1079 }
1080 }
1081
1082 public void addQuerySpaces(Serializable[] spaces) {
1083 querySpaces.addAll( Arrays.asList( spaces ) );
1084 }
1085
1086 public Type[] getReturnTypes() {
1087 return selectClause.getQueryReturnTypes();
1088 }
1089
1090 public String[] getReturnAliases() {
1091 return selectClause.getQueryReturnAliases();
1092 }
1093
1094 public SelectClause getSelectClause() {
1095 return selectClause;
1096 }
1097
1098 public FromClause getFinalFromClause() {
1099 FromClause top = currentFromClause;
1100 while ( top.getParentFromClause() != null ) {
1101 top = top.getParentFromClause();
1102 }
1103 return top;
1104 }
1105
1106 public boolean isShallowQuery() {
1107
1108 return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery();
1109 }
1110
1111 public Map getEnabledFilters() {
1112 return queryTranslatorImpl.getEnabledFilters();
1113 }
1114
1115 public LiteralProcessor getLiteralProcessor() {
1116 return literalProcessor;
1117 }
1118
1119 public ASTPrinter getASTPrinter() {
1120 return printer;
1121 }
1122
1123 public ArrayList getParameters() {
1124 return parameters;
1125 }
1126
1127 public int getNumberOfParametersInSetClause() {
1128 return numberOfParametersInSetClause;
1129 }
1130
1131 @Override
1132 protected void evaluateAssignment(AST eq) throws SemanticException {
1133 prepareLogicOperator( eq );
1134 Queryable persister = getCurrentFromClause().getFromElement().getQueryable();
1135 evaluateAssignment( eq, persister, -1 );
1136 }
1137
1138 private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) {
1139 if ( persister.isMultiTable() ) {
1140
1141 AssignmentSpecification specification = new AssignmentSpecification( eq, persister );
1142 if ( targetIndex >= 0 ) {
1143 assignmentSpecifications.add( targetIndex, specification );
1144 }
1145 else {
1146 assignmentSpecifications.add( specification );
1147 }
1148 numberOfParametersInSetClause += specification.getParameters().length;
1149 }
1150 }
1151
1152 public ArrayList getAssignmentSpecifications() {
1153 return assignmentSpecifications;
1154 }
1155
1156 @Override
1157 protected AST createIntoClause(String path, AST propertySpec) throws SemanticException {
1158 Queryable persister = ( Queryable ) getSessionFactoryHelper().requireClassPersister( path );
1159
1160 IntoClause intoClause = ( IntoClause ) getASTFactory().create( INTO, persister.getEntityName() );
1161 intoClause.setFirstChild( propertySpec );
1162 intoClause.initialize( persister );
1163
1164 addQuerySpaces( persister.getQuerySpaces() );
1165
1166 return intoClause;
1167 }
1168
1169 @Override
1170 protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException {
1171 UpdateStatement updateStatement = ( UpdateStatement ) updateNode;
1172 FromClause fromClause = updateStatement.getFromClause();
1173 if ( versioned != null ) {
1174
1175 Queryable persister = fromClause.getFromElement().getQueryable();
1176 if ( !persister.isVersioned() ) {
1177 throw new SemanticException( "increment option specified for update of non-versioned entity" );
1178 }
1179
1180 VersionType versionType = persister.getVersionType();
1181 if ( versionType instanceof UserVersionType ) {
1182 throw new SemanticException( "user-defined version types not supported for increment option" );
1183 }
1184
1185 AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" );
1186 AST versionPropertyNode = generateVersionPropertyNode( persister );
1187
1188 eq.setFirstChild( versionPropertyNode );
1189
1190 AST versionIncrementNode = null;
1191 if ( isTimestampBasedVersion( versionType ) ) {
1192 versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
1193 ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
1194 ( ( ParameterNode ) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
1195 parameters.add( 0, paramSpec );
1196 }
1197 else {
1198
1199
1200 versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" );
1201 versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) );
1202 versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) );
1203 }
1204
1205 eq.addChild( versionIncrementNode );
1206
1207 evaluateAssignment( eq, persister, 0 );
1208
1209 AST setClause = updateStatement.getSetClause();
1210 AST currentFirstSetElement = setClause.getFirstChild();
1211 setClause.setFirstChild( eq );
1212 eq.setNextSibling( currentFirstSetElement );
1213 }
1214 }
1215
1216 private boolean isTimestampBasedVersion(VersionType versionType) {
1217 final Class javaType = versionType.getReturnedClass();
1218 return Date.class.isAssignableFrom( javaType )
1219 || Calendar.class.isAssignableFrom( javaType );
1220 }
1221
1222 private AST generateVersionPropertyNode(Queryable persister) throws SemanticException {
1223 String versionPropertyName = persister.getPropertyNames()[ persister.getVersionProperty() ];
1224 AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName );
1225 AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef );
1226 resolve( versionPropertyNode );
1227 return versionPropertyNode;
1228 }
1229
1230 @Override
1231 protected void prepareLogicOperator(AST operator) throws SemanticException {
1232 ( ( OperatorNode ) operator ).initialize();
1233 }
1234
1235 @Override
1236 protected void prepareArithmeticOperator(AST operator) throws SemanticException {
1237 ( ( OperatorNode ) operator ).initialize();
1238 }
1239
1240 @Override
1241 protected void validateMapPropertyExpression(AST node) throws SemanticException {
1242 try {
1243 FromReferenceNode fromReferenceNode = (FromReferenceNode) node;
1244 QueryableCollection collectionPersister = fromReferenceNode.getFromElement().getQueryableCollection();
1245 if ( ! Map.class.isAssignableFrom( collectionPersister.getCollectionType().getReturnedClass() ) ) {
1246 throw new SemanticException( "node did not reference a map" );
1247 }
1248 }
1249 catch ( SemanticException se ) {
1250 throw se;
1251 }
1252 catch ( Throwable t ) {
1253 throw new SemanticException( "node did not reference a map" );
1254 }
1255 }
1256
1257 public static void panic() {
1258 throw new QueryException( "TreeWalker: panic" );
1259 }
1260 }