Save This Page
Home » Hibernate-3.3.2.GA » org.hibernate » hql » ast » tree » [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.tree;
   26   
   27   import java.lang.reflect.Constructor;
   28   import java.util.ArrayList;
   29   import java.util.Iterator;
   30   import java.util.List;
   31   
   32   import org.hibernate.hql.antlr.SqlTokenTypes;
   33   import org.hibernate.hql.ast.util.ASTAppender;
   34   import org.hibernate.hql.ast.util.ASTIterator;
   35   import org.hibernate.hql.ast.util.ASTPrinter;
   36   import org.hibernate.type.Type;
   37   import org.hibernate.QueryException;
   38   
   39   import antlr.SemanticException;
   40   import antlr.collections.AST;
   41   
   42   /**
   43    * Represents the list of expressions in a SELECT clause.
   44    *
   45    * @author josh
   46    */
   47   public class SelectClause extends SelectExpressionList {
   48   
   49   	private boolean prepared = false;
   50   	private boolean scalarSelect;
   51   
   52   	private List fromElementsForLoad = new ArrayList();
   53   	//private Type[] sqlResultTypes;
   54   	private Type[] queryReturnTypes;
   55   	private String[][] columnNames;
   56   	private ConstructorNode constructorNode;
   57   	private List collectionFromElements;
   58   	private String[] aliases;
   59   
   60   	/**
   61   	 * Does this SelectClause represent a scalar query
   62   	 *
   63   	 * @return True if this is a scalara select clause; false otherwise.
   64   	 */
   65   	public boolean isScalarSelect() {
   66   		return scalarSelect;
   67   	}
   68   
   69   	public boolean isDistinct() {
   70   		return getFirstChild() != null && getFirstChild().getType() == SqlTokenTypes.DISTINCT;
   71   	}
   72   
   73   	/**
   74   	 * FromElements which need to be accounted for in the load phase (either for return or for fetch).
   75   	 *
   76   	 * @return List of appropriate FromElements.
   77   	 */
   78   	public List getFromElementsForLoad() {
   79   		return fromElementsForLoad;
   80   	}
   81   
   82   	/*
   83   	 * The types represented in the SQL result set.
   84   	 *
   85   	 * @return The types represented in the SQL result set.
   86   	 */
   87   	/*public Type[] getSqlResultTypes() {
   88   		return sqlResultTypes;
   89   	}*/
   90   
   91   	/**
   92   	 * The types actually being returned from this query at the "object level".
   93   	 *
   94   	 * @return The query return types.
   95   	 */
   96   	public Type[] getQueryReturnTypes() {
   97   		return queryReturnTypes;
   98   	}
   99   	
  100   	/**
  101   	 * The HQL aliases, or generated aliases
  102   	 */
  103   	public String[] getQueryReturnAliases() {
  104   		return aliases;
  105   	}
  106   
  107   	/**
  108   	 * The column alias names being used in the generated SQL.
  109   	 *
  110   	 * @return The SQL column aliases.
  111   	 */
  112   	public String[][] getColumnNames() {
  113   		return columnNames;
  114   	}
  115   
  116   	/**
  117   	 * The constructor to use for dynamic instantiation queries.
  118   	 *
  119   	 * @return The appropriate Constructor reference, or null if not a
  120   	 *         dynamic instantiation query.
  121   	 */
  122   	public Constructor getConstructor() {
  123   		return constructorNode == null ? null : constructorNode.getConstructor();
  124   	}
  125   	
  126   	public boolean isMap() {
  127   		return constructorNode == null ? false : constructorNode.isMap();
  128   	}
  129   	
  130   	public boolean isList() {
  131   		return constructorNode == null ? false : constructorNode.isList();
  132   	}
  133   	
  134   	/**
  135   	 * Prepares an explicitly defined select clause.
  136   	 *
  137   	 * @param fromClause The from clause linked to this select clause.
  138   	 * @throws SemanticException
  139   	 */
  140   	public void initializeExplicitSelectClause(FromClause fromClause) throws SemanticException {
  141   		if ( prepared ) {
  142   			throw new IllegalStateException( "SelectClause was already prepared!" );
  143   		}
  144   
  145   		//explicit = true;	// This is an explict Select.
  146   		//ArrayList sqlResultTypeList = new ArrayList();
  147   		ArrayList queryReturnTypeList = new ArrayList();
  148   
  149   		// First, collect all of the select expressions.
  150   		// NOTE: This must be done *before* invoking setScalarColumnText() because setScalarColumnText()
  151   		// changes the AST!!!
  152   		SelectExpression[] selectExpressions = collectSelectExpressions();
  153   		
  154   		for ( int i = 0; i < selectExpressions.length; i++ ) {
  155   			SelectExpression expr = selectExpressions[i];
  156   
  157   			if ( expr.isConstructor() ) {
  158   				constructorNode = ( ConstructorNode ) expr;
  159   				List constructorArgumentTypeList = constructorNode.getConstructorArgumentTypeList();
  160   				//sqlResultTypeList.addAll( constructorArgumentTypeList );
  161   				queryReturnTypeList.addAll( constructorArgumentTypeList );
  162   				scalarSelect = true;
  163   			}
  164   			else {
  165   				Type type = expr.getDataType();
  166   				if ( type == null ) {
  167   					throw new IllegalStateException( "No data type for node: " + expr.getClass().getName() + " "
  168   							+ new ASTPrinter( SqlTokenTypes.class ).showAsString( ( AST ) expr, "" ) );
  169   				}
  170   				//sqlResultTypeList.add( type );
  171   
  172   				// If the data type is not an association type, it could not have been in the FROM clause.
  173   				if ( expr.isScalar() ) {
  174   					scalarSelect = true;
  175   				}
  176   
  177   				if ( isReturnableEntity( expr ) ) {
  178   					fromElementsForLoad.add( expr.getFromElement() );
  179   				}
  180   
  181   				// Always add the type to the return type list.
  182   				queryReturnTypeList.add( type );
  183   			}
  184   		}
  185   
  186   		//init the aliases, after initing the constructornode
  187   		initAliases(selectExpressions);
  188   
  189   		if ( !getWalker().isShallowQuery() ) {
  190   			// add the fetched entities
  191   			List fromElements = fromClause.getProjectionList();
  192   	
  193   			ASTAppender appender = new ASTAppender( getASTFactory(), this );	// Get ready to start adding nodes.
  194   			int size = fromElements.size();
  195   	
  196   			Iterator iterator = fromElements.iterator();
  197   			for ( int k = 0; iterator.hasNext(); k++ ) {
  198   				FromElement fromElement = ( FromElement ) iterator.next();
  199   	
  200   				if ( fromElement.isFetch() ) {
  201   					FromElement origin = null;
  202   					if ( fromElement.getRealOrigin() == null ) {
  203   						// work around that crazy issue where the tree contains
  204   						// "empty" FromElements (no text); afaict, this is caused
  205   						// by FromElementFactory.createCollectionJoin()
  206   						if ( fromElement.getOrigin() == null ) {
  207   							throw new QueryException( "Unable to determine origin of join fetch [" + fromElement.getDisplayText() + "]" );
  208   						}
  209   						else {
  210   							origin = fromElement.getOrigin();
  211   						}
  212   					}
  213   					else {
  214   						origin = fromElement.getRealOrigin();
  215   					}
  216   					if ( !fromElementsForLoad.contains( origin ) ) {
  217   						throw new QueryException(
  218   								"query specified join fetching, but the owner " +
  219   								"of the fetched association was not present in the select list " +
  220   								"[" + fromElement.getDisplayText() + "]"
  221   						);
  222   					}
  223   					Type type = fromElement.getSelectType();
  224   					addCollectionFromElement( fromElement );
  225   					if ( type != null ) {
  226   						boolean collectionOfElements = fromElement.isCollectionOfValuesOrComponents();
  227   						if ( !collectionOfElements ) {
  228   							// Add the type to the list of returned sqlResultTypes.
  229   							fromElement.setIncludeSubclasses( true );
  230   							fromElementsForLoad.add( fromElement );
  231   							//sqlResultTypeList.add( type );
  232   							// Generate the select expression.
  233   							String text = fromElement.renderIdentifierSelect( size, k );
  234   							SelectExpressionImpl generatedExpr = ( SelectExpressionImpl ) appender.append( SqlTokenTypes.SELECT_EXPR, text, false );
  235   							if ( generatedExpr != null ) {
  236   								generatedExpr.setFromElement( fromElement );
  237   							}
  238   						}
  239   					}
  240   				}
  241   			}
  242   	
  243   			// generate id select fragment and then property select fragment for
  244   			// each expression, just like generateSelectFragments().
  245   			renderNonScalarSelects( collectSelectExpressions(), fromClause );
  246   		}
  247   
  248   		if ( scalarSelect || getWalker().isShallowQuery() ) {
  249   			// If there are any scalars (non-entities) selected, render the select column aliases.
  250   			renderScalarSelects( selectExpressions, fromClause );
  251   		}
  252   
  253   		finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
  254   	}
  255   
  256   	private void finishInitialization(/*ArrayList sqlResultTypeList,*/ ArrayList queryReturnTypeList) {
  257   		//sqlResultTypes = ( Type[] ) sqlResultTypeList.toArray( new Type[sqlResultTypeList.size()] );
  258   		queryReturnTypes = ( Type[] ) queryReturnTypeList.toArray( new Type[queryReturnTypeList.size()] );
  259   		initializeColumnNames();
  260   		prepared = true;
  261   	}
  262   
  263   	private void initializeColumnNames() {
  264   		// Generate an 2d array of column names, the first dimension is parallel with the
  265   		// return types array.  The second dimension is the list of column names for each
  266   		// type.
  267   
  268   		// todo: we should really just collect these from the various SelectExpressions, rather than regenerating here
  269   		columnNames = getSessionFactoryHelper().generateColumnNames( queryReturnTypes );
  270   	}
  271   
  272   	/**
  273   	 * Prepares a derived (i.e., not explicitly defined in the query) select clause.
  274   	 *
  275   	 * @param fromClause The from clause to which this select clause is linked.
  276   	 */
  277   	public void initializeDerivedSelectClause(FromClause fromClause) throws SemanticException {
  278   		if ( prepared ) {
  279   			throw new IllegalStateException( "SelectClause was already prepared!" );
  280   		}
  281   		//Used to be tested by the TCK but the test is no longer here
  282   //		if ( getSessionFactoryHelper().isStrictJPAQLComplianceEnabled() && !getWalker().isSubQuery() ) {
  283   //			// NOTE : the isSubQuery() bit is a temporary hack...
  284   //			throw new QuerySyntaxException( "JPA-QL compliance requires select clause" );
  285   //		}
  286   		List fromElements = fromClause.getProjectionList();
  287   
  288   		ASTAppender appender = new ASTAppender( getASTFactory(), this );	// Get ready to start adding nodes.
  289   		int size = fromElements.size();
  290   		ArrayList sqlResultTypeList = new ArrayList( size );
  291   		ArrayList queryReturnTypeList = new ArrayList( size );
  292   
  293   		Iterator iterator = fromElements.iterator();
  294   		for ( int k = 0; iterator.hasNext(); k++ ) {
  295   			FromElement fromElement = ( FromElement ) iterator.next();
  296   			Type type = fromElement.getSelectType();
  297   
  298   			addCollectionFromElement( fromElement );
  299   
  300   			if ( type != null ) {
  301   				boolean collectionOfElements = fromElement.isCollectionOfValuesOrComponents();
  302   				if ( !collectionOfElements ) {
  303   					if ( !fromElement.isFetch() ) {
  304   						// Add the type to the list of returned sqlResultTypes.
  305   						queryReturnTypeList.add( type );
  306   					}
  307   					fromElementsForLoad.add( fromElement );
  308   					sqlResultTypeList.add( type );
  309   					// Generate the select expression.
  310   					String text = fromElement.renderIdentifierSelect( size, k );
  311   					SelectExpressionImpl generatedExpr = ( SelectExpressionImpl ) appender.append( SqlTokenTypes.SELECT_EXPR, text, false );
  312   					if ( generatedExpr != null ) {
  313   						generatedExpr.setFromElement( fromElement );
  314   					}
  315   				}
  316   			}
  317   		}
  318   
  319   		// Get all the select expressions (that we just generated) and render the select.
  320   		SelectExpression[] selectExpressions = collectSelectExpressions();
  321   
  322   		if ( getWalker().isShallowQuery() ) {
  323   			renderScalarSelects( selectExpressions, fromClause );
  324   		}
  325   		else {
  326   			renderNonScalarSelects( selectExpressions, fromClause );
  327   		}
  328   		finishInitialization( /*sqlResultTypeList,*/ queryReturnTypeList );
  329   	}
  330   	
  331   	public static boolean VERSION2_SQL = false;
  332   
  333   	private void addCollectionFromElement(FromElement fromElement) {
  334   		if ( fromElement.isFetch() ) {
  335   			if ( fromElement.isCollectionJoin() || fromElement.getQueryableCollection() != null ) {
  336   				String suffix;
  337   				if (collectionFromElements==null) {
  338   					collectionFromElements = new ArrayList();
  339   					suffix = VERSION2_SQL ? "__" : "0__";
  340   				}
  341   				else {
  342   					suffix = Integer.toString( collectionFromElements.size() ) + "__";
  343   				}
  344   				collectionFromElements.add( fromElement );
  345   				fromElement.setCollectionSuffix( suffix );
  346   			}
  347   		}
  348   	}
  349   
  350   	protected AST getFirstSelectExpression() {
  351   		AST n = getFirstChild();
  352   		// Skip 'DISTINCT' and 'ALL', so we return the first expression node.
  353   		while ( n != null && ( n.getType() == SqlTokenTypes.DISTINCT || n.getType() == SqlTokenTypes.ALL ) ) {
  354   			n = n.getNextSibling();
  355   		}
  356   		return n;
  357   	}
  358   
  359   	private boolean isReturnableEntity(SelectExpression selectExpression) throws SemanticException {
  360   		FromElement fromElement = selectExpression.getFromElement();
  361   		boolean isFetchOrValueCollection = fromElement != null && 
  362   				( fromElement.isFetch() || fromElement.isCollectionOfValuesOrComponents() ); 
  363   		if ( isFetchOrValueCollection ) {
  364   			return false;
  365   		}
  366   		else {
  367   			return selectExpression.isReturnableEntity();
  368   		}
  369   	}
  370   
  371   	private void renderScalarSelects(SelectExpression[] se, FromClause currentFromClause) throws SemanticException {
  372   		if ( !currentFromClause.isSubQuery() ) {
  373   			for ( int i = 0; i < se.length; i++ ) {
  374   				SelectExpression expr = se[i];
  375   				expr.setScalarColumnText( i );	// Create SQL_TOKEN nodes for the columns.
  376   			}
  377   		}
  378   	}
  379   	
  380   	private void initAliases(SelectExpression[] selectExpressions) {
  381   		if (constructorNode==null) {
  382   			aliases = new String[selectExpressions.length];
  383   			for ( int i=0; i<selectExpressions.length; i++ ) {
  384   				String alias = selectExpressions[i].getAlias();
  385   				aliases[i] = alias==null ? Integer.toString(i) : alias;
  386   			}
  387   		}
  388   		else {
  389   			aliases = constructorNode.getAliases();
  390   		}
  391   	}
  392   
  393   	private void renderNonScalarSelects(SelectExpression[] selectExpressions, FromClause currentFromClause) 
  394   	throws SemanticException {
  395   		ASTAppender appender = new ASTAppender( getASTFactory(), this );
  396   		final int size = selectExpressions.length;
  397   		int nonscalarSize = 0;
  398   		for ( int i = 0; i < size; i++ ) {
  399   			if ( !selectExpressions[i].isScalar() ) nonscalarSize++;
  400   		}
  401   
  402   		int j = 0;
  403   		for ( int i = 0; i < size; i++ ) {
  404   			if ( !selectExpressions[i].isScalar() ) {
  405   				SelectExpression expr = selectExpressions[i];
  406   				FromElement fromElement = expr.getFromElement();
  407   				if ( fromElement != null ) {
  408   					renderNonScalarIdentifiers( fromElement, nonscalarSize, j, expr, appender );
  409   					j++;
  410   				}
  411   			}
  412   		}
  413   
  414   		if ( !currentFromClause.isSubQuery() ) {
  415   			// Generate the property select tokens.
  416   			int k = 0;
  417   			for ( int i = 0; i < size; i++ ) {
  418   				if ( !selectExpressions[i].isScalar() ) {
  419   					FromElement fromElement = selectExpressions[i].getFromElement();
  420   					if ( fromElement != null ) {
  421   						renderNonScalarProperties( appender, fromElement, nonscalarSize, k );
  422   						k++;
  423   					}
  424   				}
  425   			}
  426   		}
  427   	}
  428   
  429   	private void renderNonScalarIdentifiers(FromElement fromElement, int nonscalarSize, int j, SelectExpression expr, ASTAppender appender) {
  430   		String text = fromElement.renderIdentifierSelect( nonscalarSize, j );
  431   		if ( !fromElement.getFromClause().isSubQuery() ) {
  432   			if ( !scalarSelect && !getWalker().isShallowQuery() ) {
  433   				//TODO: is this a bit ugly?
  434   				expr.setText( text );
  435   			}
  436   			else {
  437   				appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
  438   			}
  439   		}
  440   	}
  441   
  442   	private void renderNonScalarProperties(ASTAppender appender, FromElement fromElement, int nonscalarSize, int k) {
  443   		String text = fromElement.renderPropertySelect( nonscalarSize, k );
  444   		appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
  445   		if ( fromElement.getQueryableCollection() != null && fromElement.isFetch() ) {
  446   			text = fromElement.renderCollectionSelectFragment( nonscalarSize, k );
  447   			appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
  448   		}
  449   		// Look through the FromElement's children to find any collections of values that should be fetched...
  450   		ASTIterator iter = new ASTIterator( fromElement );
  451   		while ( iter.hasNext() ) {
  452   			FromElement child = ( FromElement ) iter.next();
  453   			if ( child.isCollectionOfValuesOrComponents() && child.isFetch() ) {
  454   				// Need a better way to define the suffixes here...
  455   				text = child.renderValueCollectionSelectFragment( nonscalarSize, nonscalarSize + k );
  456   				appender.append( SqlTokenTypes.SQL_TOKEN, text, false );
  457   			}
  458   		}
  459   	}
  460   
  461   	public List getCollectionFromElements() {
  462   		return collectionFromElements;
  463   	}
  464   }

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