Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » hql » ast » [javadoc | source]
    1   /*
    2    * Hibernate, Relational Persistence for Idiomatic Java
    3    *
    4    * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
    5    * indicated by the @author tags or express copyright attribution
    6    * statements applied by the authors.  All third-party contributions are
    7    * distributed under license by Red Hat Middleware LLC.
    8    *
    9    * This copyrighted material is made available to anyone wishing to use, modify,
   10    * copy, or redistribute it subject to the terms and conditions of the GNU
   11    * Lesser General Public License, as published by the Free Software Foundation.
   12    *
   13    * This program is distributed in the hope that it will be useful,
   14    * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   15    * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
   16    * for more details.
   17    *
   18    * You should have received a copy of the GNU Lesser General Public License
   19    * along with this distribution; if not, write to:
   20    * Free Software Foundation, Inc.
   21    * 51 Franklin Street, Fifth Floor
   22    * Boston, MA  02110-1301  USA
   23    *
   24    */
   25   package org.hibernate.hql.ast;
   26   
   27   import java.io.Serializable;
   28   import java.util.ArrayList;
   29   import java.util.Date;
   30   import java.util.HashMap;
   31   import java.util.HashSet;
   32   import java.util.Iterator;
   33   import java.util.List;
   34   import java.util.Map;
   35   import java.util.Set;
   36   
   37   import org.slf4j.Logger;
   38   import org.slf4j.LoggerFactory;
   39   import org.hibernate.QueryException;
   40   import org.hibernate.HibernateException;
   41   import org.hibernate.engine.JoinSequence;
   42   import org.hibernate.engine.ParameterBinder;
   43   import org.hibernate.engine.SessionFactoryImplementor;
   44   import org.hibernate.hql.QueryTranslator;
   45   import org.hibernate.hql.antlr.HqlSqlBaseWalker;
   46   import org.hibernate.hql.antlr.HqlSqlTokenTypes;
   47   import org.hibernate.hql.antlr.HqlTokenTypes;
   48   import org.hibernate.hql.antlr.SqlTokenTypes;
   49   import org.hibernate.hql.ast.tree.AssignmentSpecification;
   50   import org.hibernate.hql.ast.tree.CollectionFunction;
   51   import org.hibernate.hql.ast.tree.ConstructorNode;
   52   import org.hibernate.hql.ast.tree.DeleteStatement;
   53   import org.hibernate.hql.ast.tree.DotNode;
   54   import org.hibernate.hql.ast.tree.FromClause;
   55   import org.hibernate.hql.ast.tree.FromElement;
   56   import org.hibernate.hql.ast.tree.FromReferenceNode;
   57   import org.hibernate.hql.ast.tree.IdentNode;
   58   import org.hibernate.hql.ast.tree.IndexNode;
   59   import org.hibernate.hql.ast.tree.InsertStatement;
   60   import org.hibernate.hql.ast.tree.IntoClause;
   61   import org.hibernate.hql.ast.tree.MethodNode;
   62   import org.hibernate.hql.ast.tree.ParameterNode;
   63   import org.hibernate.hql.ast.tree.QueryNode;
   64   import org.hibernate.hql.ast.tree.ResolvableNode;
   65   import org.hibernate.hql.ast.tree.RestrictableStatement;
   66   import org.hibernate.hql.ast.tree.SelectClause;
   67   import org.hibernate.hql.ast.tree.SelectExpression;
   68   import org.hibernate.hql.ast.tree.UpdateStatement;
   69   import org.hibernate.hql.ast.tree.OperatorNode;
   70   import org.hibernate.hql.ast.tree.ParameterContainer;
   71   import org.hibernate.hql.ast.util.ASTPrinter;
   72   import org.hibernate.hql.ast.util.ASTUtil;
   73   import org.hibernate.hql.ast.util.AliasGenerator;
   74   import org.hibernate.hql.ast.util.JoinProcessor;
   75   import org.hibernate.hql.ast.util.LiteralProcessor;
   76   import org.hibernate.hql.ast.util.SessionFactoryHelper;
   77   import org.hibernate.hql.ast.util.SyntheticAndFactory;
   78   import org.hibernate.hql.ast.util.NodeTraverser;
   79   import org.hibernate.id.IdentifierGenerator;
   80   import org.hibernate.id.PostInsertIdentifierGenerator;
   81   import org.hibernate.id.SequenceGenerator;
   82   import org.hibernate.param.NamedParameterSpecification;
   83   import org.hibernate.param.ParameterSpecification;
   84   import org.hibernate.param.PositionalParameterSpecification;
   85   import org.hibernate.param.VersionTypeSeedParameterSpecification;
   86   import org.hibernate.param.CollectionFilterKeyParameterSpecification;
   87   import org.hibernate.persister.collection.QueryableCollection;
   88   import org.hibernate.persister.entity.Queryable;
   89   import org.hibernate.sql.JoinFragment;
   90   import org.hibernate.type.AssociationType;
   91   import org.hibernate.type.Type;
   92   import org.hibernate.type.VersionType;
   93   import org.hibernate.type.DbTimestampType;
   94   import org.hibernate.usertype.UserVersionType;
   95   import org.hibernate.util.ArrayHelper;
   96   import org.hibernate.util.StringHelper;
   97   
   98   import antlr.ASTFactory;
   99   import antlr.RecognitionException;
  100   import antlr.SemanticException;
  101   import antlr.collections.AST;
  102   
  103   /**
  104    * Implements methods used by the HQL->SQL tree transform grammar (a.k.a. the second phase).
  105    * <ul>
  106    * <li>Isolates the Hibernate API-specific code from the ANTLR generated code.</li>
  107    * <li>Handles the SQL framgents generated by the persisters in order to create the SELECT and FROM clauses,
  108    * taking into account the joins and projections that are implied by the mappings (persister/queryable).</li>
  109    * <li>Uses SqlASTFactory to create customized AST nodes.</li>
  110    * </ul>
  111    *
  112    * @see SqlASTFactory
  113    */
  114   public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
  115   	private static final Logger log = LoggerFactory.getLogger( HqlSqlWalker.class );
  116   
  117   	private final QueryTranslatorImpl queryTranslatorImpl;
  118   	private final HqlParser hqlParser;
  119   	private final SessionFactoryHelper sessionFactoryHelper;
  120   	private final Map tokenReplacements;
  121   	private final AliasGenerator aliasGenerator = new AliasGenerator();
  122   	private final LiteralProcessor literalProcessor;
  123   	private final ParseErrorHandler parseErrorHandler;
  124   	private final ASTPrinter printer;
  125   	private final String collectionFilterRole;
  126   
  127   	private FromClause currentFromClause = null;
  128   	private SelectClause selectClause;
  129   
  130   	private Set querySpaces = new HashSet();
  131   
  132   	private int parameterCount;
  133   	private Map namedParameters = new HashMap();
  134   	private ArrayList parameters = new ArrayList();
  135   	private int numberOfParametersInSetClause;
  136   	private int positionalParameterCount;
  137   
  138   	private ArrayList assignmentSpecifications = new ArrayList();
  139   
  140   	private int impliedJoinType;
  141   
  142   	/**
  143   	 * Create a new tree transformer.
  144   	 *
  145   	 * @param qti Back pointer to the query translator implementation that is using this tree transform.
  146   	 * @param sfi The session factory implementor where the Hibernate mappings can be found.
  147   	 * @param parser A reference to the phase-1 parser
  148   	 * @param tokenReplacements Registers the token replacement map with the walker.  This map will
  149   	 * be used to substitute function names and constants.
  150   	 * @param collectionRole The collection role name of the collection used as the basis for the
  151   	 * filter, NULL if this is not a collection filter compilation.
  152   	 */
  153   	public HqlSqlWalker(
  154   			QueryTranslatorImpl qti,
  155   			SessionFactoryImplementor sfi,
  156   			HqlParser parser,
  157   			Map tokenReplacements,
  158   			String collectionRole) {
  159   		setASTFactory( new SqlASTFactory( this ) );
  160   		// Initialize the error handling delegate.
  161   		this.parseErrorHandler = new ErrorCounter();
  162   		this.queryTranslatorImpl = qti;
  163   		this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
  164   		this.literalProcessor = new LiteralProcessor( this );
  165   		this.tokenReplacements = tokenReplacements;
  166   		this.collectionFilterRole = collectionRole;
  167   		this.hqlParser = parser;
  168   		this.printer = new ASTPrinter( SqlTokenTypes.class );
  169   	}
  170   
  171   
  172   	// handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  173   
  174       private int traceDepth = 0;
  175   
  176   	public void traceIn(String ruleName, AST tree) {
  177   		if ( inputState.guessing > 0 ) {
  178   			return;
  179   		}
  180   		String prefix = StringHelper.repeat( '-', (traceDepth++ * 2) ) + "-> ";
  181   		String traceText = ruleName + " (" + buildTraceNodeName(tree) + ")";
  182   		trace( prefix + traceText );
  183   	}
  184   
  185   	private String buildTraceNodeName(AST tree) {
  186   		return tree == null
  187   				? "???"
  188   				: tree.getText() + " [" + printer.getTokenTypeName( tree.getType() ) + "]";
  189   	}
  190   
  191   	public void traceOut(String ruleName, AST tree) {
  192   		if ( inputState.guessing > 0 ) {
  193   			return;
  194   		}
  195   		String prefix = "<-" + StringHelper.repeat( '-', (--traceDepth * 2) ) + " ";
  196   		trace( prefix + ruleName );
  197   	}
  198   
  199   	private void trace(String msg) {
  200   		log.trace( msg );
  201   	}
  202   
  203   
  204   	// semantic action handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  205   
  206   	protected void prepareFromClauseInputTree(AST fromClauseInput) {
  207   		if ( !isSubQuery() ) {
  208   //			// inject param specifications to account for dynamic filter param values
  209   //			if ( ! getEnabledFilters().isEmpty() ) {
  210   //				Iterator filterItr = getEnabledFilters().values().iterator();
  211   //				while ( filterItr.hasNext() ) {
  212   //					FilterImpl filter = ( FilterImpl ) filterItr.next();
  213   //					if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
  214   //						Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
  215   //						while ( paramItr.hasNext() ) {
  216   //							String parameterName = ( String ) paramItr.next();
  217   //							// currently param filters *only* work with single-column parameter types;
  218   //							// if that limitation is ever lifted, this logic will need to change to account for that
  219   //							ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
  220   //							DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
  221   //									filter.getName(),
  222   //									parameterName,
  223   //									filter.getFilterDefinition().getParameterType( parameterName ),
  224   //									 positionalParameterCount++
  225   //							);
  226   //							collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
  227   //							parameters.add( paramSpec );
  228   //						}
  229   //					}
  230   //				}
  231   //			}
  232   
  233   			if ( isFilter() ) {
  234   				// Handle collection-fiter compilation.
  235   				// IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
  236   				QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
  237   				Type collectionElementType = persister.getElementType();
  238   				if ( !collectionElementType.isEntityType() ) {
  239   					throw new QueryException( "collection of values in filter: this" );
  240   				}
  241   
  242   				String collectionElementEntityName = persister.getElementPersister().getEntityName();
  243   				ASTFactory inputAstFactory = hqlParser.getASTFactory();
  244   				AST fromElement = inputAstFactory.create( FILTER_ENTITY, collectionElementEntityName );
  245   				ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
  246   				fromClauseInput.addChild( fromElement );
  247   				// Show the modified AST.
  248   				if ( log.isDebugEnabled() ) {
  249   					log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
  250   				}
  251   				queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
  252   
  253   				// Create a parameter specification for the collection filter...
  254   				Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
  255   				ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
  256   				CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
  257   						collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
  258   				);
  259   				collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
  260   				parameters.add( collectionFilterKeyParameterSpec );
  261   			}
  262   		}
  263   	}
  264   
  265   	public boolean isFilter() {
  266   		return collectionFilterRole != null;
  267   	}
  268   
  269   	public String getCollectionFilterRole() {
  270   		return collectionFilterRole;
  271   	}
  272   
  273   	public SessionFactoryHelper getSessionFactoryHelper() {
  274   		return sessionFactoryHelper;
  275   	}
  276   
  277   	public Map getTokenReplacements() {
  278   		return tokenReplacements;
  279   	}
  280   
  281   	public AliasGenerator getAliasGenerator() {
  282   		return aliasGenerator;
  283   	}
  284   
  285   	public FromClause getCurrentFromClause() {
  286   		return currentFromClause;
  287   	}
  288   
  289   	public ParseErrorHandler getParseErrorHandler() {
  290   		return parseErrorHandler;
  291   	}
  292   
  293   	public void reportError(RecognitionException e) {
  294   		parseErrorHandler.reportError( e ); // Use the delegate.
  295   	}
  296   
  297   	public void reportError(String s) {
  298   		parseErrorHandler.reportError( s ); // Use the delegate.
  299   	}
  300   
  301   	public void reportWarning(String s) {
  302   		parseErrorHandler.reportWarning( s );
  303   	}
  304   
  305   	/**
  306   	 * Returns the set of unique query spaces (a.k.a.
  307   	 * table names) that occurred in the query.
  308   	 *
  309   	 * @return A set of table names (Strings).
  310   	 */
  311   	public Set getQuerySpaces() {
  312   		return querySpaces;
  313   	}
  314   
  315   	protected AST createFromElement(String path, AST alias, AST propertyFetch) throws SemanticException {
  316   		FromElement fromElement = currentFromClause.addFromElement( path, alias );
  317   		fromElement.setAllPropertyFetch(propertyFetch!=null);
  318   		return fromElement;
  319   	}
  320   
  321   	protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
  322   		FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
  323   		FromClause fromClause = fromElement.getFromClause();
  324   		QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
  325   		// Get the names of the columns used to link between the collection
  326   		// owner and the collection elements.
  327   		String[] keyColumnNames = persister.getKeyColumnNames();
  328   		String fkTableAlias = persister.isOneToMany()
  329   				? fromElement.getTableAlias()
  330   				: fromClause.getAliasGenerator().createName( collectionFilterRole );
  331   		JoinSequence join = sessionFactoryHelper.createJoinSequence();
  332   		join.setRoot( persister, fkTableAlias );
  333   		if ( !persister.isOneToMany() ) {
  334   			join.addJoin( ( AssociationType ) persister.getElementType(),
  335   					fromElement.getTableAlias(),
  336   					JoinFragment.INNER_JOIN,
  337   					persister.getElementColumnNames( fkTableAlias ) );
  338   		}
  339   		join.addCondition( fkTableAlias, keyColumnNames, " = ?" );
  340   		fromElement.setJoinSequence( join );
  341   		fromElement.setFilter( true );
  342   		if ( log.isDebugEnabled() ) {
  343   			log.debug( "createFromFilterElement() : processed filter FROM element." );
  344   		}
  345   		return fromElement;
  346   	}
  347   
  348   	protected void createFromJoinElement(
  349   	        AST path,
  350   	        AST alias,
  351   	        int joinType,
  352   	        AST fetchNode,
  353   	        AST propertyFetch,
  354   	        AST with) throws SemanticException {
  355   		boolean fetch = fetchNode != null;
  356   		if ( fetch && isSubQuery() ) {
  357   			throw new QueryException( "fetch not allowed in subquery from-elements" );
  358   		}
  359   		// The path AST should be a DotNode, and it should have been evaluated already.
  360   		if ( path.getType() != SqlTokenTypes.DOT ) {
  361   			throw new SemanticException( "Path expected for join!" );
  362   		}
  363   		DotNode dot = ( DotNode ) path;
  364   		int hibernateJoinType = JoinProcessor.toHibernateJoinType( joinType );
  365   		dot.setJoinType( hibernateJoinType );	// Tell the dot node about the join type.
  366   		dot.setFetch( fetch );
  367   		// Generate an explicit join for the root dot node.   The implied joins will be collected and passed up
  368   		// to the root dot node.
  369   		dot.resolve( true, false, alias == null ? null : alias.getText() );
  370   		FromElement fromElement = dot.getImpliedJoin();
  371   		fromElement.setAllPropertyFetch(propertyFetch!=null);
  372   
  373   		if ( with != null ) {
  374   			if ( fetch ) {
  375   				throw new SemanticException( "with-clause not allowed on fetched associations; use filters" );
  376   			}
  377   			handleWithFragment( fromElement, with );
  378   		}
  379   
  380   		if ( log.isDebugEnabled() ) {
  381   			log.debug( "createFromJoinElement() : " + getASTPrinter().showAsString( fromElement, "-- join tree --" ) );
  382   		}
  383   	}
  384   	private void handleWithFragment(FromElement fromElement, AST hqlWithNode) throws SemanticException {
  385   		try {
  386   			withClause( hqlWithNode );
  387   			AST hqlSqlWithNode = returnAST;
  388   			if ( log.isDebugEnabled() ) {
  389   				log.debug( "handleWithFragment() : " + getASTPrinter().showAsString( hqlSqlWithNode, "-- with clause --" ) );
  390   			}
  391   			WithClauseVisitor visitor = new WithClauseVisitor( fromElement );
  392   			NodeTraverser traverser = new NodeTraverser( visitor );
  393   			traverser.traverseDepthFirst( hqlSqlWithNode );
  394   
  395   			String withClauseJoinAlias = visitor.getJoinAlias();
  396   			if ( withClauseJoinAlias == null ) {
  397   				withClauseJoinAlias = fromElement.getCollectionTableAlias();
  398   			}
  399   			else {
  400   				FromElement referencedFromElement = visitor.getReferencedFromElement();
  401   				if ( referencedFromElement != fromElement ) {
  402   					throw new InvalidWithClauseException( "with-clause expressions did not reference from-clause element to which the with-clause was associated" );
  403   				}
  404   			}
  405   
  406   			SqlGenerator sql = new SqlGenerator( getSessionFactoryHelper().getFactory() );
  407   			sql.whereExpr( hqlSqlWithNode.getFirstChild() );
  408   
  409   			fromElement.setWithClauseFragment( withClauseJoinAlias, "(" + sql.getSQL() + ")" );
  410   		}
  411   		catch( SemanticException e ) {
  412   			throw e;
  413   		}
  414   		catch( InvalidWithClauseException e ) {
  415   			throw e;
  416   		}
  417   		catch ( Exception e) {
  418   			throw new SemanticException( e.getMessage() );
  419   		}
  420   	}
  421   
  422   	private static class WithClauseVisitor implements NodeTraverser.VisitationStrategy {
  423   		private final FromElement joinFragment;
  424   		private FromElement referencedFromElement;
  425   		private String joinAlias;
  426   
  427   		public WithClauseVisitor(FromElement fromElement) {
  428   			this.joinFragment = fromElement;
  429   		}
  430   
  431   		public void visit(AST node) {
  432   			// todo : currently expects that the individual with expressions apply to the same sql table join.
  433   			//      This may not be the case for joined-subclass where the property values
  434   			//      might be coming from different tables in the joined hierarchy.  At some
  435   			//      point we should expand this to support that capability.  However, that has
  436   			//      some difficulties:
  437   			//          1) the biggest is how to handle ORs when the individual comparisons are
  438   			//              linked to different sql joins.
  439   			//          2) here we would need to track each comparison individually, along with
  440   			//              the join alias to which it applies and then pass that information
  441   			//              back to the FromElement so it can pass it along to the JoinSequence
  442   			if ( node instanceof DotNode ) {
  443   				DotNode dotNode = ( DotNode ) node;
  444   				FromElement fromElement = dotNode.getFromElement();
  445   				if ( referencedFromElement != null ) {
  446   					if ( fromElement != referencedFromElement ) {
  447   						throw new HibernateException( "with-clause referenced two different from-clause elements" );
  448   					}
  449   				}
  450   				else {
  451   					referencedFromElement = fromElement;
  452   					joinAlias = extractAppliedAlias( dotNode );
  453   					// todo : temporary
  454   					//      needed because currently persister is the one that
  455   					//      creates and renders the join fragments for inheritence
  456   					//      hierarchies...
  457   					if ( !joinAlias.equals( referencedFromElement.getTableAlias() ) ) {
  458   						throw new InvalidWithClauseException( "with clause can only reference columns in the driving table" );
  459   					}
  460   				}
  461   			}
  462   			else if ( node instanceof ParameterNode ) {
  463   				applyParameterSpecification( ( ( ParameterNode ) node ).getHqlParameterSpecification() );
  464   			}
  465   			else if ( node instanceof ParameterContainer ) {
  466   				applyParameterSpecifications( ( ParameterContainer ) node );
  467   			}
  468   		}
  469   
  470   		private void applyParameterSpecifications(ParameterContainer parameterContainer) {
  471   			if ( parameterContainer.hasEmbeddedParameters() ) {
  472   				ParameterSpecification[] specs = parameterContainer.getEmbeddedParameters();
  473   				for ( int i = 0; i < specs.length; i++ ) {
  474   					applyParameterSpecification( specs[i] );
  475   				}
  476   			}
  477   		}
  478   
  479   		private void applyParameterSpecification(ParameterSpecification paramSpec) {
  480   			joinFragment.addEmbeddedParameter( paramSpec );
  481   		}
  482   
  483   		private String extractAppliedAlias(DotNode dotNode) {
  484   			return dotNode.getText().substring( 0, dotNode.getText().indexOf( '.' ) );
  485   		}
  486   
  487   		public FromElement getReferencedFromElement() {
  488   			return referencedFromElement;
  489   		}
  490   
  491   		public String getJoinAlias() {
  492   			return joinAlias;
  493   		}
  494   	}
  495   
  496   	/**
  497   	 * Sets the current 'FROM' context.
  498   	 *
  499   	 * @param fromNode      The new 'FROM' context.
  500   	 * @param inputFromNode The from node from the input AST.
  501   	 */
  502   	protected void pushFromClause(AST fromNode, AST inputFromNode) {
  503   		FromClause newFromClause = ( FromClause ) fromNode;
  504   		newFromClause.setParentFromClause( currentFromClause );
  505   		currentFromClause = newFromClause;
  506   	}
  507   
  508   	/**
  509   	 * Returns to the previous 'FROM' context.
  510   	 */
  511   	private void popFromClause() {
  512   		currentFromClause = currentFromClause.getParentFromClause();
  513   	}
  514   
  515   	protected void lookupAlias(AST aliasRef)
  516   			throws SemanticException {
  517   		FromElement alias = currentFromClause.getFromElement( aliasRef.getText() );
  518   		FromReferenceNode aliasRefNode = ( FromReferenceNode ) aliasRef;
  519   		aliasRefNode.setFromElement( alias );
  520   	}
  521   
  522   	protected void setImpliedJoinType(int joinType) {
  523   		impliedJoinType = JoinProcessor.toHibernateJoinType( joinType );
  524   	}
  525   
  526   	public int getImpliedJoinType() {
  527   		return impliedJoinType;
  528   	}
  529   
  530   	protected AST lookupProperty(AST dot, boolean root, boolean inSelect) throws SemanticException {
  531   		DotNode dotNode = ( DotNode ) dot;
  532   		FromReferenceNode lhs = dotNode.getLhs();
  533   		AST rhs = lhs.getNextSibling();
  534   		switch ( rhs.getType() ) {
  535   			case SqlTokenTypes.ELEMENTS:
  536   			case SqlTokenTypes.INDICES:
  537   				if ( log.isDebugEnabled() ) {
  538   					log.debug( "lookupProperty() " + dotNode.getPath() + " => " + rhs.getText() + "(" + lhs.getPath() + ")" );
  539   				}
  540   				CollectionFunction f = ( CollectionFunction ) rhs;
  541   				// Re-arrange the tree so that the collection function is the root and the lhs is the path.
  542   				f.setFirstChild( lhs );
  543   				lhs.setNextSibling( null );
  544   				dotNode.setFirstChild( f );
  545   				resolve( lhs );			// Don't forget to resolve the argument!
  546   				f.resolve( inSelect );	// Resolve the collection function now.
  547   				return f;
  548   			default:
  549   				// Resolve everything up to this dot, but don't resolve the placeholders yet.
  550   				dotNode.resolveFirstChild();
  551   				return dotNode;
  552   		}
  553   	}
  554   
  555   	protected boolean isNonQualifiedPropertyRef(AST ident) {
  556   		final String identText = ident.getText();
  557   		if ( currentFromClause.isFromElementAlias( identText ) ) {
  558   			return false;
  559   		}
  560   
  561   		List fromElements = currentFromClause.getExplicitFromElements();
  562   		if ( fromElements.size() == 1 ) {
  563   			final FromElement fromElement = ( FromElement ) fromElements.get( 0 );
  564   			try {
  565   				log.trace( "attempting to resolve property [" + identText + "] as a non-qualified ref" );
  566   				return fromElement.getPropertyMapping( identText ).toType( identText ) != null;
  567   			}
  568   			catch( QueryException e ) {
  569   				// Should mean that no such property was found
  570   			}
  571   		}
  572   
  573   		return false;
  574   	}
  575   
  576   	protected AST lookupNonQualifiedProperty(AST property) throws SemanticException {
  577   		final FromElement fromElement = ( FromElement ) currentFromClause.getExplicitFromElements().get( 0 );
  578   		AST syntheticDotNode = generateSyntheticDotNodeForNonQualifiedPropertyRef( property, fromElement );
  579   		return lookupProperty( syntheticDotNode, false, getCurrentClauseType() == HqlSqlTokenTypes.SELECT );
  580   	}
  581   
  582   	private AST generateSyntheticDotNodeForNonQualifiedPropertyRef(AST property, FromElement fromElement) {
  583   		AST dot = getASTFactory().create( DOT, "{non-qualified-property-ref}" );
  584   		// TODO : better way?!?
  585   		( ( DotNode ) dot ).setPropertyPath( ( ( FromReferenceNode ) property ).getPath() );
  586   
  587   		IdentNode syntheticAlias = ( IdentNode ) getASTFactory().create( IDENT, "{synthetic-alias}" );
  588   		syntheticAlias.setFromElement( fromElement );
  589   		syntheticAlias.setResolved();
  590   
  591   		dot.setFirstChild( syntheticAlias );
  592   		dot.addChild( property );
  593   
  594   		return dot;
  595   	}
  596   
  597   	protected void processQuery(AST select, AST query) throws SemanticException {
  598   		if ( log.isDebugEnabled() ) {
  599   			log.debug( "processQuery() : " + query.toStringTree() );
  600   		}
  601   
  602   		try {
  603   			QueryNode qn = ( QueryNode ) query;
  604   
  605   			// Was there an explicit select expression?
  606   			boolean explicitSelect = select != null && select.getNumberOfChildren() > 0;
  607   
  608   			if ( !explicitSelect ) {
  609   				// No explicit select expression; render the id and properties
  610   				// projection lists for every persister in the from clause into
  611   				// a single 'token node'.
  612   				//TODO: the only reason we need this stuff now is collection filters,
  613   				//      we should get rid of derived select clause completely!
  614   				createSelectClauseFromFromClause( qn );
  615   			}
  616   			else {
  617   				// Use the explicitly declared select expression; determine the
  618   				// return types indicated by each select token
  619   				useSelectClause( select );
  620   			}
  621   
  622   			// After that, process the JOINs.
  623   			// Invoke a delegate to do the work, as this is farily complex.
  624   			JoinProcessor joinProcessor = new JoinProcessor( this );
  625   			joinProcessor.processJoins( qn );
  626   
  627   			// Attach any mapping-defined "ORDER BY" fragments
  628   			Iterator itr = qn.getFromClause().getProjectionList().iterator();
  629   			while ( itr.hasNext() ) {
  630   				final FromElement fromElement = ( FromElement ) itr.next();
  631   //			if ( fromElement.isFetch() && fromElement.isCollectionJoin() ) {
  632   				if ( fromElement.isFetch() && fromElement.getQueryableCollection() != null ) {
  633   					// Does the collection referenced by this FromElement
  634   					// specify an order-by attribute?  If so, attach it to
  635   					// the query's order-by
  636   					if ( fromElement.getQueryableCollection().hasOrdering() ) {
  637   						String orderByFragment = fromElement
  638   								.getQueryableCollection()
  639   								.getSQLOrderByString( fromElement.getCollectionTableAlias() );
  640   						qn.getOrderByClause().addOrderFragment( orderByFragment );
  641   					}
  642   					if ( fromElement.getQueryableCollection().hasManyToManyOrdering() ) {
  643   						String orderByFragment = fromElement.getQueryableCollection()
  644   								.getManyToManyOrderByString( fromElement.getTableAlias() );
  645   						qn.getOrderByClause().addOrderFragment( orderByFragment );
  646   					}
  647   				}
  648   			}
  649   		}
  650   		finally {
  651   			popFromClause();
  652   		}
  653   	}
  654   
  655   	protected void postProcessDML(RestrictableStatement statement) throws SemanticException {
  656   		statement.getFromClause().resolve();
  657   
  658   		FromElement fromElement = ( FromElement ) statement.getFromClause().getFromElements().get( 0 );
  659   		Queryable persister = fromElement.getQueryable();
  660   		// Make #@%$^#^&# sure no alias is applied to the table name
  661   		fromElement.setText( persister.getTableName() );
  662   
  663   //		// append any filter fragments; the EMPTY_MAP is used under the assumption that
  664   //		// currently enabled filters should not affect this process
  665   //		if ( persister.getDiscriminatorType() != null ) {
  666   //			new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment(
  667   //			        statement,
  668   //			        persister,
  669   //			        java.util.Collections.EMPTY_MAP,
  670   //			        fromElement.getTableAlias()
  671   //			);
  672   //		}
  673   		if ( persister.getDiscriminatorType() != null || ! queryTranslatorImpl.getEnabledFilters().isEmpty() ) {
  674   			new SyntheticAndFactory( this ).addDiscriminatorWhereFragment(
  675   			        statement,
  676   			        persister,
  677   			        queryTranslatorImpl.getEnabledFilters(),
  678   			        fromElement.getTableAlias()
  679   			);
  680   		}
  681   
  682   	}
  683   
  684   	protected void postProcessUpdate(AST update) throws SemanticException {
  685   		UpdateStatement updateStatement = ( UpdateStatement ) update;
  686   
  687   		postProcessDML( updateStatement );
  688   	}
  689   
  690   	protected void postProcessDelete(AST delete) throws SemanticException {
  691   		postProcessDML( ( DeleteStatement ) delete );
  692   	}
  693   
  694   	public static boolean supportsIdGenWithBulkInsertion(IdentifierGenerator generator) {
  695   		return SequenceGenerator.class.isAssignableFrom( generator.getClass() )
  696   		        || PostInsertIdentifierGenerator.class.isAssignableFrom( generator.getClass() );
  697   	}
  698   
  699   	protected void postProcessInsert(AST insert) throws SemanticException, QueryException {
  700   		InsertStatement insertStatement = ( InsertStatement ) insert;
  701   		insertStatement.validate();
  702   
  703   		SelectClause selectClause = insertStatement.getSelectClause();
  704   		Queryable persister = insertStatement.getIntoClause().getQueryable();
  705   
  706   		if ( !insertStatement.getIntoClause().isExplicitIdInsertion() ) {
  707   			// We need to generate ids as part of this bulk insert.
  708   			//
  709   			// Note that this is only supported for sequence-style generators and
  710   			// post-insert-style generators; basically, only in-db generators
  711   			IdentifierGenerator generator = persister.getIdentifierGenerator();
  712   			if ( !supportsIdGenWithBulkInsertion( generator ) ) {
  713   				throw new QueryException( "can only generate ids as part of bulk insert with either sequence or post-insert style generators" );
  714   			}
  715   
  716   			AST idSelectExprNode = null;
  717   
  718   			if ( SequenceGenerator.class.isAssignableFrom( generator.getClass() ) ) {
  719   				String seqName = ( String ) ( ( SequenceGenerator ) generator ).generatorKey();
  720   				String nextval = sessionFactoryHelper.getFactory().getDialect().getSelectSequenceNextValString( seqName );
  721   				idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, nextval );
  722   			}
  723   			else {
  724   				//Don't need this, because we should never ever be selecting no columns in an insert ... select...
  725   				//and because it causes a bug on DB2
  726   				/*String idInsertString = sessionFactoryHelper.getFactory().getDialect().getIdentityInsertString();
  727   				if ( idInsertString != null ) {
  728   					idSelectExprNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, idInsertString );
  729   				}*/
  730   			}
  731   
  732   			if ( idSelectExprNode != null ) {
  733   				AST currentFirstSelectExprNode = selectClause.getFirstChild();
  734   				selectClause.setFirstChild( idSelectExprNode );
  735   				idSelectExprNode.setNextSibling( currentFirstSelectExprNode );
  736   
  737   				insertStatement.getIntoClause().prependIdColumnSpec();
  738   			}
  739   		}
  740   
  741   		final boolean includeVersionProperty = persister.isVersioned() &&
  742   				!insertStatement.getIntoClause().isExplicitVersionInsertion() &&
  743   				persister.isVersionPropertyInsertable();
  744   		if ( includeVersionProperty ) {
  745   			// We need to seed the version value as part of this bulk insert
  746   			VersionType versionType = persister.getVersionType();
  747   			AST versionValueNode = null;
  748   
  749   			if ( sessionFactoryHelper.getFactory().getDialect().supportsParametersInInsertSelect() ) {
  750   				int sqlTypes[] = versionType.sqlTypes( sessionFactoryHelper.getFactory() );
  751   				if ( sqlTypes == null || sqlTypes.length == 0 ) {
  752   					throw new IllegalStateException( versionType.getClass() + ".sqlTypes() returns null or empty array" );
  753   				}
  754   				if ( sqlTypes.length > 1 ) {
  755   					throw new IllegalStateException(
  756   							versionType.getClass() +
  757   									".sqlTypes() returns > 1 element; only single-valued versions are allowed."
  758   					);
  759   				}
  760   				versionValueNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
  761   				ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
  762   				( ( ParameterNode ) versionValueNode ).setHqlParameterSpecification( paramSpec );
  763   				parameters.add( 0, paramSpec );
  764   
  765   				if ( sessionFactoryHelper.getFactory().getDialect().requiresCastingOfParametersInSelectClause() ) {
  766   					// we need to wrtap the param in a cast()
  767   					MethodNode versionMethodNode = ( MethodNode ) getASTFactory().create( HqlSqlTokenTypes.METHOD_CALL, "(" );
  768   					AST methodIdentNode = getASTFactory().create( HqlSqlTokenTypes.IDENT, "cast" );
  769   					versionMethodNode.addChild( methodIdentNode );
  770   					versionMethodNode.initializeMethodNode(methodIdentNode, true );
  771   					AST castExprListNode = getASTFactory().create( HqlSqlTokenTypes.EXPR_LIST, "exprList" );
  772   					methodIdentNode.setNextSibling( castExprListNode );
  773   					castExprListNode.addChild( versionValueNode );
  774   					versionValueNode.setNextSibling(
  775   							getASTFactory().create(
  776   									HqlSqlTokenTypes.IDENT,
  777   									sessionFactoryHelper.getFactory().getDialect().getTypeName( sqlTypes[0] ) )
  778   					);
  779   					processFunction( versionMethodNode, true );
  780   					versionValueNode = versionMethodNode;
  781   				}
  782   			}
  783   			else {
  784   				if ( isIntegral( versionType ) ) {
  785   					try {
  786   						Object seedValue = versionType.seed( null );
  787   						versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, seedValue.toString() );
  788   					}
  789   					catch( Throwable t ) {
  790   						throw new QueryException( "could not determine seed value for version on bulk insert [" + versionType + "]" );
  791   					}
  792   				}
  793   				else if ( isDatabaseGeneratedTimestamp( versionType ) ) {
  794   					String functionName = sessionFactoryHelper.getFactory().getDialect().getCurrentTimestampSQLFunctionName();
  795   					versionValueNode = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, functionName );
  796   				}
  797   				else {
  798   					throw new QueryException( "cannot handle version type [" + versionType + "] on bulk inserts with dialects not supporting parameters in insert-select statements" );
  799   				}
  800   			}
  801   
  802   			AST currentFirstSelectExprNode = selectClause.getFirstChild();
  803   			selectClause.setFirstChild( versionValueNode );
  804   			versionValueNode.setNextSibling( currentFirstSelectExprNode );
  805   
  806   			insertStatement.getIntoClause().prependVersionColumnSpec();
  807   		}
  808   
  809   		if ( insertStatement.getIntoClause().isDiscriminated() ) {
  810   			String sqlValue = insertStatement.getIntoClause().getQueryable().getDiscriminatorSQLValue();
  811   			AST discrimValue = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, sqlValue );
  812   			insertStatement.getSelectClause().addChild( discrimValue );
  813   		}
  814   
  815   	}
  816   
  817   	private boolean isDatabaseGeneratedTimestamp(Type type) {
  818   		// currently only the Hibernate-supplied DbTimestampType is supported here
  819   		return DbTimestampType.class.isAssignableFrom( type.getClass() );
  820   	}
  821   
  822   	private boolean isIntegral(Type type) {
  823   		return Long.class.isAssignableFrom( type.getReturnedClass() )
  824   		       || Integer.class.isAssignableFrom( type.getReturnedClass() )
  825   		       || long.class.isAssignableFrom( type.getReturnedClass() )
  826   		       || int.class.isAssignableFrom( type.getReturnedClass() );
  827   	}
  828   
  829   	private void useSelectClause(AST select) throws SemanticException {
  830   		selectClause = ( SelectClause ) select;
  831   		selectClause.initializeExplicitSelectClause( currentFromClause );
  832   	}
  833   
  834   	private void createSelectClauseFromFromClause(QueryNode qn) throws SemanticException {
  835   		AST select = astFactory.create( SELECT_CLAUSE, "{derived select clause}" );
  836   		AST sibling = qn.getFromClause();
  837   		qn.setFirstChild( select );
  838   		select.setNextSibling( sibling );
  839   		selectClause = ( SelectClause ) select;
  840   		selectClause.initializeDerivedSelectClause( currentFromClause );
  841   		if ( log.isDebugEnabled() ) {
  842   			log.debug( "Derived SELECT clause created." );
  843   		}
  844   	}
  845   
  846   	protected void resolve(AST node) throws SemanticException {
  847   		if ( node != null ) {
  848   			// This is called when it's time to fully resolve a path expression.
  849   			ResolvableNode r = ( ResolvableNode ) node;
  850   			if ( isInFunctionCall() ) {
  851   				r.resolveInFunctionCall( false, true );
  852   			}
  853   			else {
  854   				r.resolve( false, true );	// Generate implicit joins, only if necessary.
  855   			}
  856   		}
  857   	}
  858   
  859   	protected void resolveSelectExpression(AST node) throws SemanticException {
  860   		// This is called when it's time to fully resolve a path expression.
  861   		int type = node.getType();
  862   		switch ( type ) {
  863   			case DOT:
  864   				DotNode dot = ( DotNode ) node;
  865   				dot.resolveSelectExpression();
  866   				break;
  867   			case ALIAS_REF:
  868   				// Notify the FROM element that it is being referenced by the select.
  869   				FromReferenceNode aliasRefNode = ( FromReferenceNode ) node;
  870   				//aliasRefNode.resolve( false, false, aliasRefNode.getText() ); //TODO: is it kosher to do it here?
  871   				aliasRefNode.resolve( false, false ); //TODO: is it kosher to do it here?
  872   				FromElement fromElement = aliasRefNode.getFromElement();
  873   				if ( fromElement != null ) {
  874   					fromElement.setIncludeSubclasses( true );
  875   				}
  876   			default:
  877   				break;
  878   		}
  879   	}
  880   
  881   	protected void beforeSelectClause() throws SemanticException {
  882   		// Turn off includeSubclasses on all FromElements.
  883   		FromClause from = getCurrentFromClause();
  884   		List fromElements = from.getFromElements();
  885   		for ( Iterator iterator = fromElements.iterator(); iterator.hasNext(); ) {
  886   			FromElement fromElement = ( FromElement ) iterator.next();
  887   			fromElement.setIncludeSubclasses( false );
  888   		}
  889   	}
  890   
  891   	protected AST generatePositionalParameter(AST inputNode) throws SemanticException {
  892   		if ( namedParameters.size() > 0 ) {
  893   			throw new SemanticException( "cannot define positional parameter after any named parameters have been defined" );
  894   		}
  895   		ParameterNode parameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
  896   		PositionalParameterSpecification paramSpec = new PositionalParameterSpecification(
  897   				inputNode.getLine(),
  898   		        inputNode.getColumn(),
  899   				positionalParameterCount++
  900   		);
  901   		parameter.setHqlParameterSpecification( paramSpec );
  902   		parameters.add( paramSpec );
  903   		return parameter;
  904   	}
  905   
  906   	protected AST generateNamedParameter(AST delimiterNode, AST nameNode) throws SemanticException {
  907   		String name = nameNode.getText();
  908   		trackNamedParameterPositions( name );
  909   
  910   		// create the node initially with the param name so that it shows
  911   		// appropriately in the "original text" attribute
  912   		ParameterNode parameter = ( ParameterNode ) astFactory.create( NAMED_PARAM, name );
  913   		parameter.setText( "?" );
  914   
  915   		NamedParameterSpecification paramSpec = new NamedParameterSpecification(
  916   				delimiterNode.getLine(),
  917   		        delimiterNode.getColumn(),
  918   				name
  919   		);
  920   		parameter.setHqlParameterSpecification( paramSpec );
  921   		parameters.add( paramSpec );
  922   		return parameter;
  923   	}
  924   
  925   	private void trackNamedParameterPositions(String name) {
  926   		Integer loc = new Integer( parameterCount++ );
  927   		Object o = namedParameters.get( name );
  928   		if ( o == null ) {
  929   			namedParameters.put( name, loc );
  930   		}
  931   		else if ( o instanceof Integer ) {
  932   			ArrayList list = new ArrayList( 4 );
  933   			list.add( o );
  934   			list.add( loc );
  935   			namedParameters.put( name, list );
  936   		}
  937   		else {
  938   			( ( ArrayList ) o ).add( loc );
  939   		}
  940   	}
  941   
  942   	protected void processConstant(AST constant) throws SemanticException {
  943   		literalProcessor.processConstant( constant, true );  // Use the delegate, resolve identifiers as FROM element aliases.
  944   	}
  945   
  946   	protected void processBoolean(AST constant) throws SemanticException {
  947   		literalProcessor.processBoolean( constant );  // Use the delegate.
  948   	}
  949   
  950   	protected void processNumericLiteral(AST literal) {
  951   		literalProcessor.processNumeric( literal );
  952   	}
  953   
  954   	protected void processIndex(AST indexOp) throws SemanticException {
  955   		IndexNode indexNode = ( IndexNode ) indexOp;
  956   		indexNode.resolve( true, true );
  957   	}
  958   
  959   	protected void processFunction(AST functionCall, boolean inSelect) throws SemanticException {
  960   		MethodNode methodNode = ( MethodNode ) functionCall;
  961   		methodNode.resolve( inSelect );
  962   	}
  963   
  964   	protected void processConstructor(AST constructor) throws SemanticException {
  965   		ConstructorNode constructorNode = ( ConstructorNode ) constructor;
  966   		constructorNode.prepare();
  967   	}
  968   
  969       protected void setAlias(AST selectExpr, AST ident) {
  970           ((SelectExpression) selectExpr).setAlias(ident.getText());
  971       }
  972   
  973   	/**
  974   	 * Returns the locations of all occurrences of the named parameter.
  975   	 */
  976   	public int[] getNamedParameterLocations(String name) throws QueryException {
  977   		Object o = namedParameters.get( name );
  978   		if ( o == null ) {
  979   			QueryException qe = new QueryException( QueryTranslator.ERROR_NAMED_PARAMETER_DOES_NOT_APPEAR + name );
  980   			qe.setQueryString( queryTranslatorImpl.getQueryString() );
  981   			throw qe;
  982   		}
  983   		if ( o instanceof Integer ) {
  984   			return new int[]{( ( Integer ) o ).intValue()};
  985   		}
  986   		else {
  987   			return ArrayHelper.toIntArray( ( ArrayList ) o );
  988   		}
  989   	}
  990   
  991   	public void addQuerySpaces(Serializable[] spaces) {
  992   		for ( int i = 0; i < spaces.length; i++ ) {
  993   			querySpaces.add( spaces[i] );
  994   		}
  995   	}
  996   
  997   	public Type[] getReturnTypes() {
  998   		return selectClause.getQueryReturnTypes();
  999   	}
 1000   
 1001   	public String[] getReturnAliases() {
 1002   		return selectClause.getQueryReturnAliases();
 1003   	}
 1004   
 1005   	public SelectClause getSelectClause() {
 1006   		return selectClause;
 1007   	}
 1008   	
 1009   	public FromClause getFinalFromClause() {
 1010   		FromClause top = currentFromClause;
 1011   		while ( top.getParentFromClause() != null ) {
 1012   			top = top.getParentFromClause();
 1013   		}
 1014   		return top;
 1015   	}
 1016   
 1017   	public boolean isShallowQuery() {
 1018   		// select clauses for insert statements should alwasy be treated as shallow
 1019   		return getStatementType() == INSERT || queryTranslatorImpl.isShallowQuery();
 1020   	}
 1021   
 1022   	public Map getEnabledFilters() {
 1023   		return queryTranslatorImpl.getEnabledFilters();
 1024   	}
 1025   
 1026   	public LiteralProcessor getLiteralProcessor() {
 1027   		return literalProcessor;
 1028   	}
 1029   
 1030   	public ASTPrinter getASTPrinter() {
 1031   		return printer;
 1032   	}
 1033   
 1034   	public ArrayList getParameters() {
 1035   		return parameters;
 1036   	}
 1037   
 1038   	public int getNumberOfParametersInSetClause() {
 1039   		return numberOfParametersInSetClause;
 1040   	}
 1041   
 1042   	protected void evaluateAssignment(AST eq) throws SemanticException {
 1043   		prepareLogicOperator( eq );
 1044   		Queryable persister = getCurrentFromClause().getFromElement().getQueryable();
 1045   		evaluateAssignment( eq, persister, -1 );
 1046   	}
 1047   
 1048   	private void evaluateAssignment(AST eq, Queryable persister, int targetIndex) {
 1049   		if ( persister.isMultiTable() ) {
 1050   			// no need to even collect this information if the persister is considered multi-table
 1051   			AssignmentSpecification specification = new AssignmentSpecification( eq, persister );
 1052   			if ( targetIndex >= 0 ) {
 1053   				assignmentSpecifications.add( targetIndex, specification );
 1054   			}
 1055   			else {
 1056   				assignmentSpecifications.add( specification );
 1057   			}
 1058   			numberOfParametersInSetClause += specification.getParameters().length;
 1059   		}
 1060   	}
 1061   
 1062   	public ArrayList getAssignmentSpecifications() {
 1063   		return assignmentSpecifications;
 1064   	}
 1065   
 1066   	protected AST createIntoClause(String path, AST propertySpec) throws SemanticException {
 1067   		Queryable persister = ( Queryable ) getSessionFactoryHelper().requireClassPersister( path );
 1068   
 1069   		IntoClause intoClause = ( IntoClause ) getASTFactory().create( INTO, persister.getEntityName() );
 1070   		intoClause.setFirstChild( propertySpec );
 1071   		intoClause.initialize( persister );
 1072   
 1073   		addQuerySpaces( persister.getQuerySpaces() );
 1074   
 1075   		return intoClause;
 1076   	}
 1077   
 1078   	protected void prepareVersioned(AST updateNode, AST versioned) throws SemanticException {
 1079   		UpdateStatement updateStatement = ( UpdateStatement ) updateNode;
 1080   		FromClause fromClause = updateStatement.getFromClause();
 1081   		if ( versioned != null ) {
 1082   			// Make sure that the persister is versioned
 1083   			Queryable persister = fromClause.getFromElement().getQueryable();
 1084   			if ( !persister.isVersioned() ) {
 1085   				throw new SemanticException( "increment option specified for update of non-versioned entity" );
 1086   			}
 1087   
 1088   			VersionType versionType = persister.getVersionType();
 1089   			if ( versionType instanceof UserVersionType ) {
 1090   				throw new SemanticException( "user-defined version types not supported for increment option" );
 1091   			}
 1092   
 1093   			AST eq = getASTFactory().create( HqlSqlTokenTypes.EQ, "=" );
 1094   			AST versionPropertyNode = generateVersionPropertyNode( persister );
 1095   
 1096   			eq.setFirstChild( versionPropertyNode );
 1097   
 1098   			AST versionIncrementNode = null;
 1099   			if ( Date.class.isAssignableFrom( versionType.getReturnedClass() ) ) {
 1100   				versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PARAM, "?" );
 1101   				ParameterSpecification paramSpec = new VersionTypeSeedParameterSpecification( versionType );
 1102   				( ( ParameterNode ) versionIncrementNode ).setHqlParameterSpecification( paramSpec );
 1103   				parameters.add( 0, paramSpec );
 1104   			}
 1105   			else {
 1106   				// Not possible to simply re-use the versionPropertyNode here as it causes
 1107   				// OOM errors due to circularity :(
 1108   				versionIncrementNode = getASTFactory().create( HqlSqlTokenTypes.PLUS, "+" );
 1109   				versionIncrementNode.setFirstChild( generateVersionPropertyNode( persister ) );
 1110   				versionIncrementNode.addChild( getASTFactory().create( HqlSqlTokenTypes.IDENT, "1" ) );
 1111   			}
 1112   
 1113   			eq.addChild( versionIncrementNode );
 1114   
 1115   			evaluateAssignment( eq, persister, 0 );
 1116   
 1117   			AST setClause = updateStatement.getSetClause();
 1118   			AST currentFirstSetElement = setClause.getFirstChild();
 1119   			setClause.setFirstChild( eq );
 1120   			eq.setNextSibling( currentFirstSetElement );
 1121   		}
 1122   	}
 1123   
 1124   	private AST generateVersionPropertyNode(Queryable persister) throws SemanticException {
 1125   		String versionPropertyName = persister.getPropertyNames()[ persister.getVersionProperty() ];
 1126   		AST versionPropertyRef = getASTFactory().create( HqlSqlTokenTypes.IDENT, versionPropertyName );
 1127   		AST versionPropertyNode = lookupNonQualifiedProperty( versionPropertyRef );
 1128   		resolve( versionPropertyNode );
 1129   		return versionPropertyNode;
 1130   	}
 1131   
 1132   	protected void prepareLogicOperator(AST operator) throws SemanticException {
 1133   		( ( OperatorNode ) operator ).initialize();
 1134   	}
 1135   
 1136   	protected void prepareArithmeticOperator(AST operator) throws SemanticException {
 1137   		( ( OperatorNode ) operator ).initialize();
 1138   	}
 1139   
 1140   	public static void panic() {
 1141   		throw new QueryException( "TreeWalker: panic" );
 1142   	}
 1143   }

Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » hql » ast » [javadoc | source]