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

Save This Page
Home » hibernate-distribution-3.3.1.GA-dist » org.hibernate » hql » ast » [javadoc | source]