[hibernate-commits] Hibernate SVN: r10824 - in trunk/Hibernate3: src/org/hibernate/hql/ast/tree src/org/hibernate/hql/ast/util src/org/hibernate/persister/entity test/org/hibernate/test/hql

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu Nov 16 14:32:52 EST 2006


Author: steve.ebersole at jboss.com
Date: 2006-11-16 14:32:48 -0500 (Thu, 16 Nov 2006)
New Revision: 10824

Modified:
   trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/util/JoinProcessor.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/util/SessionFactoryHelper.java
   trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
   trunk/Hibernate3/src/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
   trunk/Hibernate3/src/org/hibernate/persister/entity/Queryable.java
   trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
   trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java
Log:
HHH-939  : special '.class' property reference from HQL for joined-subclass;
HHH-1631 : sub/super-classes property reference from HQL for joined-subclass

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -138,8 +138,10 @@
 		// discover foreign key (id) properties.
 		lhs.resolve( true, true, null, this );
 		setFromElement( lhs.getFromElement() );			// The 'from element' that the property is in.
+
+		checkSubclassOrSuperclassPropertyReference( lhs, propName );
 	}
-	
+
 	public void resolveInFunctionCall(boolean generateJoin, boolean implicitJoin) throws SemanticException {
 		if ( isResolved() ) {
 			return;
@@ -163,7 +165,7 @@
 		dereferenceCollection( ( CollectionType ) propertyType, true, true, null, parent );
 	}
 
-	public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent) 
+	public void resolve(boolean generateJoin, boolean implicitJoin, String classAlias, AST parent)
 	throws SemanticException {
 		// If this dot has already been resolved, stop now.
 		if ( isResolved() ) {
@@ -177,7 +179,7 @@
 			if ( parent == null ) {
 				getWalker().getLiteralProcessor().lookupConstant( this );
 			}
-			// If the propertyType is null and there isn't a parent, just 
+			// If the propertyType is null and there isn't a parent, just
 			// stop now... there was a problem resolving the node anyway.
 			return;
 		}
@@ -205,7 +207,7 @@
 		}
 		setResolved();
 	}
-	
+
 	private void initText() {
 		String[] cols = getColumns();
 		String text = StringHelper.join( ", ", cols );
@@ -222,14 +224,14 @@
 		return propertyType;
 	}
 
-	private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent) 
+	private void dereferenceCollection(CollectionType collectionType, boolean implicitJoin, boolean indexed, String classAlias, AST parent)
 	throws SemanticException {
-		
+
 		dereferenceType = DEREF_COLLECTION;
 		String role = collectionType.getRole();
-		
+
 		//foo.bars.size (also handles deprecated stuff like foo.bars.maxelement for backwardness)
-		boolean isSizeProperty = getNextSibling()!=null && 
+		boolean isSizeProperty = getNextSibling()!=null &&
 			CollectionProperties.isAnyCollectionProperty( getNextSibling().getText() );
 
 		if ( isSizeProperty ) indexed = true; //yuck!
@@ -260,19 +262,19 @@
 		        implicitJoin
 		);
 		FromElement elem = factory.createCollection( queryableCollection, role, joinType, fetch, indexed );
-		
+
 		if ( log.isDebugEnabled() ) {
 			log.debug( "dereferenceCollection() : Created new FROM element for " + propName + " : " + elem );
 		}
-		
+
 		setImpliedJoin( elem );
 		setFromElement( elem );	// This 'dot' expression now refers to the resulting from element.
-		
+
 		if ( isSizeProperty ) {
 			elem.setText("");
 			elem.setUseWhereFragment(false);
 		}
-		
+
 		if ( !implicitJoin ) {
 			EntityPersister entityPersister = elem.getEntityPersister();
 			if ( entityPersister != null ) {
@@ -284,14 +286,6 @@
 
 	private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String classAlias, boolean generateJoin, AST parent) throws SemanticException {
 		checkForCorrelatedSubquery( "dereferenceEntity" );
-		// If this is an entity inside a component reference, then generate the join.
-//		if ( unresolvedComponent( generateJoin ) ) {
-//			if ( log.isDebugEnabled() ) {
-//				log.debug( "dereferenceEntity() : resolving unresolved component '" + propertyPath + "' ... " );
-//			}
-//			dereferenceEntityJoin( classAlias, entityType, implicitJoin, parent );
-//			return;
-//		}
 
 		// Only join to the entity table if:
 		//      1) we were instructed to generate any needed joins (generateJoins==true)
@@ -324,24 +318,11 @@
 
 	}
 
-	private boolean unresolvedComponent(boolean generateJoin) {
-		AST c = getFirstChild();
-		if ( generateJoin && isDotNode( c ) ) {
-			DotNode dot = ( DotNode ) c;
-			if ( dot.dereferenceType == DEREF_COMPONENT || dot.dereferenceType == DEREF_IDENTIFIER ) {
-				if ( StringHelper.isNotEmpty( propertyPath ) ) {
-					return true;
-				}
-			}
-		}
-		return false;
-	}
-
 	private boolean isDotNode(AST n) {
 		return n != null && n.getType() == SqlTokenTypes.DOT;
 	}
 
-	private void dereferenceEntityJoin(String classAlias, EntityType propertyType, boolean impliedJoin, AST parent) 
+	private void dereferenceEntityJoin(String classAlias, EntityType propertyType, boolean impliedJoin, AST parent)
 	throws SemanticException {
 		dereferenceType = DEREF_ENTITY;
 		if ( log.isDebugEnabled() ) {
@@ -403,17 +384,17 @@
 			FromElementFactory factory = new FromElementFactory(
 			        currentFromClause,
 					getLhs().getFromElement(),
-					joinPath, 
-					classAlias, 
-					joinColumns, 
+					joinPath,
+					classAlias,
+					joinColumns,
 					impliedJoin
 			);
-			elem = factory.createEntityJoin( 
-					associatedEntityName, 
-					tableAlias, 
-					joinSequence, 
-					fetch, 
-					getWalker().isInFrom(), 
+			elem = factory.createEntityJoin(
+					associatedEntityName,
+					tableAlias,
+					joinSequence,
+					fetch,
+					getWalker().isInFrom(),
 					propertyType
 			);
 		}
@@ -445,26 +426,11 @@
 			return propertyType.isReferenceToPrimaryKey();
 		}
 		else {
-			String keyPropertyName = getSessionFactoryHelper()
-			        .getIdentifierOrUniqueKeyPropertyName( propertyType );
-			return keyPropertyName != null && keyPropertyName.equals( propertyName );
+			String keyPropertyName = getSessionFactoryHelper().getIdentifierOrUniqueKeyPropertyName( propertyType );
+			return keyPropertyName != null && keyPropertyName.equals( propertyName ) && propertyType.isReferenceToPrimaryKey();
 		}
 	}
 
-//	private boolean isPrimaryKeyReference(String property, EntityType propertyType) {
-//		boolean isIdShortcut = EntityPersister.ENTITY_ID.equals( property ) &&
-//				propertyType.isReferenceToPrimaryKey();
-//		return isIdShortcut;
-//	}
-//
-//	private boolean isNamedIdPropertyShortcut(EntityType propertyType, String property) {
-//		final String idPropertyName = getSessionFactoryHelper()
-//				.getIdentifierOrUniqueKeyPropertyName( propertyType );
-//		boolean isNamedIdPropertyShortcut = idPropertyName != null &&
-//				idPropertyName.equals( property );
-//		return isNamedIdPropertyShortcut;
-//	}
-
 	private void checkForCorrelatedSubquery(String methodName) {
 		if ( isCorrelatedSubselect() ) {
 			if ( log.isDebugEnabled() ) {
@@ -494,8 +460,8 @@
 		// special shortcut for id properties, skip the join!
 		// this must only occur at the _end_ of a path expression
 		if ( log.isDebugEnabled() ) {
-			log.debug( "dereferenceShortcut() : property " + 
-				propertyName + " in " + getFromElement().getClassName() + 
+			log.debug( "dereferenceShortcut() : property " +
+				propertyName + " in " + getFromElement().getClassName() +
 				" does not require a join." );
 		}
 
@@ -614,8 +580,14 @@
 				}
 			}
 		}
+
+		FromReferenceNode lhs = getLhs();
+		while ( lhs != null ) {
+			checkSubclassOrSuperclassPropertyReference( lhs, lhs.getNextSibling().getText() );
+			lhs = ( FromReferenceNode ) lhs.getFirstChild();
+		}
 	}
-	
+
 	/**
 	 * Used ONLY for regression testing!
 	 */
@@ -626,4 +598,14 @@
 		dereferenceType = DEREF_JAVA_CONSTANT;
 		setResolved(); // Don't resolve the node again.
 	}
+
+	private boolean checkSubclassOrSuperclassPropertyReference(FromReferenceNode lhs, String propertyName) {
+		if ( lhs != null && !( lhs instanceof IndexNode ) && lhs.getDataType() != null && ! lhs.getDataType().isComponentType() ) {
+			final FromElement source = lhs.getFromElement();
+			if ( source != null ) {
+				source.handlePropertyBeingDereferenced( propertyName );
+			}
+		}
+		return false;
+	}
 }

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElement.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -7,6 +7,7 @@
 import org.hibernate.QueryException;
 import org.hibernate.engine.JoinSequence;
 import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.CollectionProperties;
 import org.hibernate.hql.antlr.SqlTokenTypes;
 import org.hibernate.hql.ast.util.ASTUtil;
 import org.hibernate.hql.ast.HqlSqlWalker;
@@ -60,6 +61,8 @@
 	private boolean manyToMany = false;
 	private String withClauseFragment = null;
 	private String withClauseJoinAlias;
+	private boolean dereferencedBySuperclassProperty;
+	private boolean dereferencedBySubclassProperty;
 
 	public FromElement() {
 	}
@@ -258,6 +261,11 @@
 	}
 
 	public void setIncludeSubclasses(boolean includeSubclasses) {
+		if ( isDereferencedBySuperclassOrSubclassProperty() ) {
+			if ( !includeSubclasses && log.isTraceEnabled() ) {
+				log.trace( "attempt to disable subclass-inclusions", new Exception( "stack-trace source" ) );
+			}
+		}
 		this.includeSubclasses = includeSubclasses;
 	}
 
@@ -265,6 +273,10 @@
 		return includeSubclasses;
 	}
 
+	public boolean isDereferencedBySuperclassOrSubclassProperty() {
+		return dereferencedBySubclassProperty || dereferencedBySuperclassProperty;
+	}
+
 	public String getIdentityColumn() {
 		checkInitialized();
 		String table = getTableAlias();
@@ -470,11 +482,11 @@
 	public boolean isFromOrJoinFragment() {
 		return getType() == SqlTokenTypes.FROM_FRAGMENT || getType() == SqlTokenTypes.JOIN_FRAGMENT;
 	}
-	
+
 	public boolean isAllPropertyFetch() {
 		return isAllPropertyFetch;
 	}
-	
+
 	public void setAllPropertyFetch(boolean fetch) {
 		isAllPropertyFetch = fetch;
 	}
@@ -500,4 +512,33 @@
 			return getQueryable().hasCache();
 		}
 	}
+
+	public void handlePropertyBeingDereferenced(String propertyName) {
+		if ( getQueryableCollection() != null && CollectionProperties.isCollectionProperty( propertyName ) ) {
+		}
+		else {
+			Queryable persister = getQueryable();
+			if ( persister != null ) {
+				Queryable.Declarer propertyDeclarer = persister.getSubclassPropertyDeclarer( propertyName );
+				if ( log.isTraceEnabled() ) {
+					log.trace( "handling property dereference [" + persister.getEntityName() + " (" + getClassAlias() + ") -> " + propertyName + " (" + propertyDeclarer + ")]" );
+				}
+				if ( propertyDeclarer == Queryable.Declarer.SUBCLASS ) {
+					dereferencedBySubclassProperty = true;
+					includeSubclasses = true;
+				}
+				else if ( propertyDeclarer == Queryable.Declarer.SUPERCLASS ) {
+					dereferencedBySuperclassProperty = true;
+				}
+			}
+		}
+	}
+
+	public boolean isDereferencedBySuperclassProperty() {
+		return dereferencedBySuperclassProperty;
+	}
+
+	public boolean isDereferencedBySubclassProperty() {
+		return dereferencedBySubclassProperty;
+	}
 }

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -38,7 +38,6 @@
 	private QueryableCollection queryableCollection;
 	private CollectionPropertyMapping collectionPropertyMapping;
 	private JoinSequence joinSequence;
-
 	private String collectionSuffix;
 
 	public FromElementType(FromElement fromElement, EntityPersister persister, EntityType entityType) {

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/util/JoinProcessor.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/util/JoinProcessor.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/util/JoinProcessor.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -5,6 +5,7 @@
 import java.util.Iterator;
 import java.util.ListIterator;
 import java.util.Collections;
+import java.util.List;
 
 import org.hibernate.AssertionFailure;
 import org.hibernate.engine.JoinSequence;
@@ -13,6 +14,7 @@
 import org.hibernate.hql.ast.tree.FromClause;
 import org.hibernate.hql.ast.tree.FromElement;
 import org.hibernate.hql.ast.tree.QueryNode;
+import org.hibernate.hql.ast.tree.DotNode;
 import org.hibernate.sql.JoinFragment;
 import org.hibernate.util.StringHelper;
 
@@ -26,7 +28,7 @@
  * The join generating classes are complex, this encapsulates some of the JoinSequence-related
  * code.
  *
- * @author josh Jul 22, 2004 7:33:42 AM
+ * @author Joshua Davis
  */
 public class JoinProcessor implements SqlTokenTypes {
 
@@ -38,7 +40,7 @@
 	/**
 	 * Constructs a new JoinProcessor.
 	 *
-	 * @param astFactory The factory for AST node creation.
+	 * @param astFactory		  The factory for AST node creation.
 	 * @param queryTranslatorImpl The query translator.
 	 */
 	public JoinProcessor(ASTFactory astFactory, QueryTranslatorImpl queryTranslatorImpl) {
@@ -70,35 +72,51 @@
 	public void processJoins(QueryNode query, boolean inSubquery) {
 		final FromClause fromClause = query.getFromClause();
 
-		// TODO : found it easiest to simply reorder the FromElements here into ascending order
-		// in terms of injecting them into the resulting sql ast in orders relative to those
-		// expected by the old parser; this is definitely another of those "only needed
-		// for regression purposes".  The SyntheticAndFactory, then, simply injects them as it
-		// encounters them.
-		ArrayList orderedFromElements = new ArrayList();
-		ListIterator liter = fromClause.getFromElements().listIterator( fromClause.getFromElements().size() );
-		while ( liter.hasPrevious() ) {
-			orderedFromElements.add( liter.previous() );
+		final List fromElements;
+		if ( DotNode.useThetaStyleImplicitJoins ) {
+			// for regression testing against output from the old parser...
+			// found it easiest to simply reorder the FromElements here into ascending order
+			// in terms of injecting them into the resulting sql ast in orders relative to those
+			// expected by the old parser; this is definitely another of those "only needed
+			// for regression purposes".  The SyntheticAndFactory, then, simply injects them as it
+			// encounters them.
+			fromElements = new ArrayList();
+			ListIterator liter = fromClause.getFromElements().listIterator( fromClause.getFromElements().size() );
+			while ( liter.hasPrevious() ) {
+				fromElements.add( liter.previous() );
+			}
 		}
+		else {
+			fromElements = fromClause.getFromElements();
+		}
 
 		// Iterate through the alias,JoinSequence pairs and generate SQL token nodes.
-		Iterator iter = orderedFromElements.iterator();
+		Iterator iter = fromElements.iterator();
 		while ( iter.hasNext() ) {
 			final FromElement fromElement = ( FromElement ) iter.next();
 			JoinSequence join = fromElement.getJoinSequence();
 			join.setSelector(
-			        new JoinSequence.Selector() {
-				        public boolean includeSubclasses(String alias) {
-					        boolean shallowQuery = queryTranslatorImpl.isShallowQuery();
-					        boolean containsTableAlias = fromClause.containsTableAlias( alias );
-					        boolean includeSubclasses = fromElement.isIncludeSubclasses();
-					        boolean subQuery = fromClause.isSubQuery();
-					        return includeSubclasses && containsTableAlias && !subQuery && !shallowQuery;
-				        }
-			        }
-				);
+					new JoinSequence.Selector() {
+						public boolean includeSubclasses(String alias) {
+							// The uber-rule here is that we need to include  subclass joins if
+							// the FromElement is in any way dereferenced by a property from
+							// the subclass table; otherwise we end up with column references
+							// qualified by a non-existent table reference in the resulting SQL...
+							boolean containsTableAlias = fromClause.containsTableAlias( alias );
+							if ( fromElement.isDereferencedBySubclassProperty() ) {
+								// TODO : or should we return 'containsTableAlias'??
+								log.trace( "forcing inclusion of extra joins [alias=" + alias + ", containsTableAlias=" + containsTableAlias + "]" );
+								return true;
+							}
+							boolean shallowQuery = queryTranslatorImpl.isShallowQuery();
+							boolean includeSubclasses = fromElement.isIncludeSubclasses();
+							boolean subQuery = fromClause.isSubQuery();
+							return includeSubclasses && containsTableAlias && !subQuery && !shallowQuery;
+						}
+					}
+			);
 			addJoinNodes( query, join, fromElement, inSubquery );
-		} // while
+		}
 
 	}
 
@@ -106,10 +124,10 @@
 		// Generate FROM and WHERE fragments for the from element.
 		JoinFragment joinFragment = join.toJoinFragment(
 				inSubquery ? Collections.EMPTY_MAP : queryTranslatorImpl.getEnabledFilters(),
-				fromElement.useFromFragment(),
-		        fromElement.getWithClauseFragment(),
-		        fromElement.getWithClauseJoinAlias()
-			);
+				fromElement.useFromFragment() || fromElement.isDereferencedBySuperclassOrSubclassProperty(),
+				fromElement.getWithClauseFragment(),
+				fromElement.getWithClauseJoinAlias()
+		);
 
 		String frag = joinFragment.toFromFragmentString();
 		String whereFrag = joinFragment.toWhereFragmentString();
@@ -126,7 +144,9 @@
 		// If there is a FROM fragment and the FROM element is an explicit, then add the from part.
 		if ( fromElement.useFromFragment() /*&& StringHelper.isNotEmpty( frag )*/ ) {
 			String fromFragment = processFromFragment( frag, join );
-			if ( log.isDebugEnabled() ) log.debug( "Using FROM fragment [" + fromFragment + "]" );
+			if ( log.isDebugEnabled() ) {
+				log.debug( "Using FROM fragment [" + fromFragment + "]" );
+			}
 			fromElement.setText( fromFragment.trim() ); // Set the text of the fromElement.
 		}
 		andFactory.addWhereFragment( joinFragment, whereFrag, query, fromElement );
@@ -138,22 +158,6 @@
 		if ( fromFragment.startsWith( ", " ) ) {
 			fromFragment = fromFragment.substring( 2 );
 		}
-		/*
-		// *** BEGIN FROM FRAGMENT VOODOO ***
-		// If there is more than one join, reverse the order of the tables in the FROM fragment.
-		if ( join.getJoinCount() > 1 && fromFragment.indexOf( ',' ) >= 0 ) {
-			String[] froms = StringHelper.split( ",", fromFragment );
-			StringBuffer buf = new StringBuffer();
-			for ( int i = froms.length - 1; i >= 0; i-- ) {
-				buf.append( froms[i] );
-				if ( i > 0 ) {
-					buf.append( ", " );
-				}
-			}
-			fromFragment = buf.toString();
-		}
-		// *** END OF FROM FRAGMENT VOODOO ***
-		*/
 		return fromFragment;
 	}
 

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/util/SessionFactoryHelper.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/util/SessionFactoryHelper.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/util/SessionFactoryHelper.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -379,12 +379,7 @@
 			}
 		}
 
-		Type rtnType = sqlFunction.getReturnType( argumentType, sfi );
-		if ( rtnType == null ) {
-			throw new QueryException( "unable to determine function return type [" + functionName + "]" );
-		}
-
-		return rtnType;
+		return sqlFunction.getReturnType( argumentType, sfi );
 	}
 
 	public String[][] generateColumnNames(Type[] sqlResultTypes) {

Modified: trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -847,14 +847,12 @@
 			final SessionImplementor session,
 			final Object[] snapshot,
 			final int j,
-			final Object propValue
-	) {
+			final Object propValue) {
 		setPropertyValue( entity, lazyPropertyNumbers[j], propValue, session.getEntityMode() );
-		
-		if (snapshot != null) { // object have been loaded with setReadOnly(true); HHH-2236
+		if (snapshot != null) {
+			// object have been loaded with setReadOnly(true); HHH-2236
 			snapshot[ lazyPropertyNumbers[j] ] = lazyPropertyTypes[j].deepCopy( propValue, session.getEntityMode(), factory );
 		}
-		
 		return fieldName.equals( lazyPropertyNames[j] );
 	}
 
@@ -1352,6 +1350,19 @@
 		return index==-1 ? 0 : getSubclassPropertyTableNumber(index);
 	}
 
+	public Declarer getSubclassPropertyDeclarer(String propertyPath) {
+		int tableIndex = getSubclassPropertyTableNumber( propertyPath );
+		if ( tableIndex == 0 ) {
+			return Declarer.CLASS;
+		}
+		else if ( isClassOrSuperclassTable( tableIndex ) ) {
+			return Declarer.SUPERCLASS;
+		}
+		else {
+			return Declarer.SUBCLASS;
+		}
+	}
+
 	protected String generateTableAlias(String rootAlias, int tableNumber) {
 		if ( tableNumber == 0 ) {
 			return rootAlias;
@@ -3674,11 +3685,11 @@
 	public Type getIdentifierType() {
 		return entityMetamodel.getIdentifierProperty().getType();
 	}
-	
+
 	public boolean hasSubselectLoadableCollections() {
 		return hasSubselectLoadableCollections;
 	}
-	
+
 	public int[] getNaturalIdentifierProperties() {
 		return entityMetamodel.getNaturalIdentifierProperties();
 	}
@@ -3769,7 +3780,7 @@
 	public boolean hasNaturalIdentifier() {
 		return entityMetamodel.hasNaturalIdentifier();
 	}
-	
+
 	public void setPropertyValue(Object object, String propertyName, Object value, EntityMode entityMode)
 			throws HibernateException {
 		getTuplizer( entityMode ).setPropertyValue( object, propertyName, value );

Modified: trunk/Hibernate3/src/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -44,7 +44,7 @@
 	private final String[][] tableKeyColumns;
 	private final String[][] naturalOrderTableKeyColumns;
 	private final boolean[] naturalOrderCascadeDeleteEnabled;
-	
+
 	private final String[] spaces;
 
 	private final String[] subclassClosure;
@@ -82,7 +82,7 @@
 	//INITIALIZATION:
 
 	public JoinedSubclassEntityPersister(
-			final PersistentClass persistentClass, 
+			final PersistentClass persistentClass,
 			final CacheConcurrencyStrategy cache,
 			final SessionFactoryImplementor factory,
 			final Mapping mapping)
@@ -109,8 +109,8 @@
 
 		if ( optimisticLockMode()!=Versioning.OPTIMISTIC_LOCK_VERSION ) {
 			throw new MappingException(
-					"optimistic-lock attribute not supported for joined-subclass mappings: " + 
-					getEntityName() 
+					"optimistic-lock attribute not supported for joined-subclass mappings: " +
+					getEntityName()
 			);
 		}
 
@@ -126,10 +126,10 @@
 		while ( titer.hasNext() ) {
 			Table tab = (Table) titer.next();
 			KeyValue key = (KeyValue) kiter.next();
-			String tabname = tab.getQualifiedName( 
-					factory.getDialect(), 
-					factory.getSettings().getDefaultCatalogName(), 
-					factory.getSettings().getDefaultSchemaName() 
+			String tabname = tab.getQualifiedName(
+					factory.getDialect(),
+					factory.getSettings().getDefaultCatalogName(),
+					factory.getSettings().getDefaultSchemaName()
 			);
 			tables.add(tabname);
 			String[] keyCols = new String[idColumnSpan];
@@ -151,10 +151,10 @@
 		while ( titer.hasNext() ) {
 			Table tab = (Table) titer.next();
 			isConcretes.add( new Boolean( persistentClass.isClassOrSuperclassTable(tab) ) );
-			String tabname = tab.getQualifiedName( 
-					factory.getDialect(), 
-					factory.getSettings().getDefaultCatalogName(), 
-					factory.getSettings().getDefaultSchemaName() 
+			String tabname = tab.getQualifiedName(
+					factory.getDialect(),
+					factory.getSettings().getDefaultCatalogName(),
+					factory.getSettings().getDefaultSchemaName()
 			);
 			subtables.add(tabname);
 			String[] key = new String[idColumnSpan];
@@ -182,8 +182,8 @@
 		reverse(subclassTableNameClosure, tableSpan);
 		reverse(subclassTableKeyColumnClosure, tableSpan);
 
-		spaces = ArrayHelper.join( 
-				tableNames, 
+		spaces = ArrayHelper.join(
+				tableNames,
 				ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() )
 		);
 
@@ -233,8 +233,8 @@
 		while( iter.hasNext() ) {
 			Property prop = (Property) iter.next();
 			String tabname = prop.getValue().getTable().getQualifiedName(
-				factory.getDialect(), 
-				factory.getSettings().getDefaultCatalogName(), 
+				factory.getDialect(),
+				factory.getSettings().getDefaultCatalogName(),
 				factory.getSettings().getDefaultSchemaName()
 			);
 			propertyTableNumbers[i] = getTableId(tabname, tableNames);
@@ -245,19 +245,19 @@
 		// subclass closure properties
 
 		//TODO: code duplication with SingleTableEntityPersister
-		
+
 		ArrayList columnTableNumbers = new ArrayList();
 		ArrayList formulaTableNumbers = new ArrayList();
 		ArrayList propTableNumbers = new ArrayList();
-		
+
 		iter = persistentClass.getSubclassPropertyClosureIterator();
 		while ( iter.hasNext() ) {
 			Property prop = (Property) iter.next();
 			Table tab = prop.getValue().getTable();
-			String tabname = tab.getQualifiedName( 
-					factory.getDialect(), 
-					factory.getSettings().getDefaultCatalogName(), 
-					factory.getSettings().getDefaultSchemaName() 
+			String tabname = tab.getQualifiedName(
+					factory.getDialect(),
+					factory.getSettings().getDefaultCatalogName(),
+					factory.getSettings().getDefaultSchemaName()
 			);
 			Integer tabnum = new Integer( getTableId(tabname, subclassTableNameClosure) );
 			propTableNumbers.add(tabnum);
@@ -290,10 +290,10 @@
 			discriminatorValues[subclassSpan-1] = discriminatorSQLString;
 			notNullColumnTableNumbers = new int[subclassSpan];
 			final int id = getTableId(
-				persistentClass.getTable().getQualifiedName( 
-						factory.getDialect(), 
-						factory.getSettings().getDefaultCatalogName(), 
-						factory.getSettings().getDefaultSchemaName() 
+				persistentClass.getTable().getQualifiedName(
+						factory.getDialect(),
+						factory.getSettings().getDefaultCatalogName(),
+						factory.getSettings().getDefaultSchemaName()
 				),
 				subclassTableNameClosure
 			);
@@ -315,16 +315,16 @@
 			try {
 				if ( persistentClass.isPolymorphic() ) {
 					// we now use subclass ids that are consistent across all
-					// persisters for a class hierarchy, so that the use of 
+					// persisters for a class hierarchy, so that the use of
 					// "foo.class = Bar" works in HQL
 					Integer subclassId = new Integer( sc.getSubclassId() );//new Integer(k+1);
 					subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() );
 					discriminatorValues[k] = subclassId.toString();
 					int id = getTableId(
-						sc.getTable().getQualifiedName( 
-								factory.getDialect(), 
-								factory.getSettings().getDefaultCatalogName(), 
-								factory.getSettings().getDefaultSchemaName() 
+						sc.getTable().getQualifiedName(
+								factory.getDialect(),
+								factory.getSettings().getDefaultCatalogName(),
+								factory.getSettings().getDefaultSchemaName()
 						),
 						subclassTableNameClosure
 					);
@@ -341,7 +341,7 @@
 		initLockers();
 
 		initSubclassPropertyAliasesMap(persistentClass);
-		
+
 		postConstruct(mapping);
 
 	}
@@ -351,7 +351,7 @@
 		//TODO: other lock modes?
 		loader = createEntityLoader(LockMode.NONE, CollectionHelper.EMPTY_MAP);
 	}*/
-	
+
 	public String getSubclassPropertyTableName(int i) {
 		return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
 	}
@@ -381,11 +381,11 @@
 	protected String[] getKeyColumns(int j) {
 		return naturalOrderTableKeyColumns[j];
 	}
-	
+
 	protected boolean isTableCascadeDeleteEnabled(int j) {
 		return naturalOrderCascadeDeleteEnabled[j];
 	}
-	
+
 	protected boolean isPropertyOfTable(int property, int j) {
 		return naturalOrderPropertyTableNumbers[property]==j;
 	}
@@ -403,7 +403,7 @@
 				getQueryLoader() :
 				this.loader;
 		try {
-			
+
 			final Object result = loader.load(id, optionalObject, session);
 
 			if (result!=null) lock(id, getVersion(result), result, lockMode, session);
@@ -451,7 +451,7 @@
 	public String getTableName() {
 		return tableNames[0];
 	}
-	
+
 	private static int getTableId(String tableName, String[] tables) {
 		for ( int j=0; j<tables.length; j++ ) {
 			if ( tableName.equals( tables[j] ) ) {
@@ -575,4 +575,12 @@
 	public String getRootTableName() {
 		return naturalOrderTableNames[0];
 	}
+
+	public Declarer getSubclassPropertyDeclarer(String propertyPath) {
+		if ( "class".equals( propertyPath ) ) {
+			// special case where we need to force incloude all subclass joins
+			return Declarer.SUBCLASS;
+		}
+		return super.getSubclassPropertyDeclarer( propertyPath );
+	}
 }

Modified: trunk/Hibernate3/src/org/hibernate/persister/entity/Queryable.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/persister/entity/Queryable.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/src/org/hibernate/persister/entity/Queryable.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -102,6 +102,18 @@
 	public int getSubclassPropertyTableNumber(String propertyPath);
 
 	/**
+	 * Determine whether the given property is declared by our
+	 * mapped class, our super class, or one of our subclasses...
+	 * <p/>
+	 * Note: the method is called 'subclass property...' simply
+	 * for consistency sake (e.g. {@link #getSubclassPropertyTableNumber}
+	 *
+	 * @param propertyPath The property name.
+	 * @return The property declarer
+	 */
+	public Declarer getSubclassPropertyDeclarer(String propertyPath);
+
+	/**
 	 * Get the name of the table with the given index from the internal
 	 * array.
 	 *
@@ -109,7 +121,7 @@
 	 * @return
 	 */
 	public String getSubclassTableName(int number);
-	
+
 	/**
 	 * Is the version property included in insert statements?
 	 */
@@ -126,4 +138,17 @@
 	 * @return The alias used for "filter conditions" within the where clause.
 	 */
 	public String generateFilterConditionAlias(String rootAlias);
+
+	public static class Declarer {
+		public static final Declarer CLASS = new Declarer( "class" );
+		public static final Declarer SUBCLASS = new Declarer( "subclass" );
+		public static final Declarer SUPERCLASS = new Declarer( "superclass" );
+		private final String name;
+		public Declarer(String name) {
+			this.name = name;
+		}
+		public String toString() {
+			return name;
+		}
+	}
 }

Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -80,7 +80,9 @@
 				"cid/Order.hbm.xml",
 				"cid/LineItem.hbm.xml",
 				"cid/Product.hbm.xml",
-				"any/Properties.hbm.xml"
+				"any/Properties.hbm.xml",
+				"legacy/Commento.hbm.xml",
+				"legacy/Marelo.hbm.xml"
 		};
 	}
 
@@ -90,92 +92,65 @@
 		cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
 	}
 
-	public void testSimpleSelectWithLimitAndOffset() throws Exception {
-		if ( ! ( getDialect().supportsLimit() && getDialect().supportsLimitOffset() ) ) {
-			reportSkip( "dialect does not support offset and limit combo", "limit and offset combination" );
-			return;
-		}
-
-		// just checking correctness of param binding code...
-		Session session = openSession();
-		session.createQuery( "from Animal" )
-				.setFirstResult( 2 )
-				.setMaxResults( 1 )
-				.list();
-		session.close();
+	public void testNestedComponentIsNull() {
+		// (1) From MapTest originally...
+		// (2) Was then moved into HQLTest...
+		// (3) However, a bug fix to EntityType#getIdentifierOrUniqueKeyType (HHH-2138)
+		// 		caused the classic parser to suddenly start throwing exceptions on
+		//		this query, apparently relying on the buggy behavior somehow; thus
+		//		moved here to at least get some syntax checking...
+		new SyntaxChecker( "from Commento c where c.marelo.commento.mcompr is null" ).checkAll();
 	}
 
-	public void testSelectAndResultColumnReferences() {
-		Session s = openSession();
-		s.beginTransaction();
-		s.createQuery( "select m.name from Model as m" ).list();
-		s.createQuery( "select h.name.first from Human h" ).list();
-		s.createQuery( "select h.name from Human h" ).list();
-		s.getTransaction().commit();
-		s.close();
+	public void testSpecialClassPropertyReference() {
+		// this is a long standing bug in Hibernate when applied to joined-subclasses;
+		//  see HHH-939 for details and history
+		new SyntaxChecker( "from Zoo zoo where zoo.class = PettingZoo" ).checkAll();
+		new SyntaxChecker( "select a.description from Animal a where a.class = Mammal" ).checkAll();
+		new SyntaxChecker( "select a.class from Animal a" ).checkAll();
+		new SyntaxChecker( "from DomesticAnimal an where an.class = Dog" ).checkAll();
+		new SyntaxChecker( "from Animal an where an.class = Dog" ).checkAll();
 	}
 
-	public void testSelectAndResultColumnReferencesWithGrouping() {
-		if ( ! allowsPhysicalColumnNameInHaving( "scalar HQL queries" ) ) {
-			return;
-		}
+	public void testSubclassOrSuperclassPropertyReferenceInJoinedSubclass() {
+		// this is a long standing bug in Hibernate; see HHH-1631 for details and history
+		//
+		// (1) pregnant is defined as a property of the class (Mammal) itself
+		// (2) description is defined as a property of the superclass (Animal)
+		// (3) name is defined as a property of a particular subclass (Human)
 
-		Session s = openSession();
-		s.beginTransaction();
+		new SyntaxChecker( "from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkIterate();
 
-		s.createQuery( "select m.name, count(*) from Model as m group by m.name" ).list();
-		s.createQuery( "select m.name, count(*) from Model as m group by m.name order by m.name" ).list();
-		s.createQuery( "select m.name, max( m.id ) from Model as m group by m.name having max( id ) > 1" ).list();
-		s.createQuery( "select m.name, count(*) from Model as m group by m.name having count(*) > 1" ).list();
-		s.createQuery( "select m.name, count(*) from Model as m group by m.name having m.name like 'abc%'" ).list();
+		new SyntaxChecker( "from Zoo z join z.mammals as m where m.pregnant = false" ).checkAll();
+		new SyntaxChecker( "select m.pregnant from Zoo z join z.mammals as m where m.pregnant = false" ).checkAll();
 
-		s.createQuery( "select h.name.first, count(*) from Human h group by h.name.first" ).list();
-		s.createQuery( "select h.name.first, count(*) from Human h group by h.name.first having count(*) > 1" ).list();
+		new SyntaxChecker( "from Zoo z join z.mammals as m where m.description = 'tabby'" ).checkAll();
+		new SyntaxChecker( "select m.description from Zoo z join z.mammals as m where m.description = 'tabby'" ).checkAll();
 
-		s.createQuery( "select h.name, count(*) from Human h group by h.name" ).list();
-		s.createQuery( "select h.name, count(*) from Human h group by h.name having count(*) > 1" ).list();
+		new SyntaxChecker( "from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkAll();
+		new SyntaxChecker( "select m.name from Zoo z join z.mammals as m where m.name.first = 'John'" ).checkAll();
 
-		s.getTransaction().commit();
-		s.close();
-	}
+		new SyntaxChecker( "select m.pregnant from Zoo z join z.mammals as m" ).checkAll();
+		new SyntaxChecker( "select m.description from Zoo z join z.mammals as m" ).checkAll();
+		new SyntaxChecker( "select m.name from Zoo z join z.mammals as m" ).checkAll();
 
-	public void testSelectAndResultColumnReferencesWithCollation() {
-		if ( ! allowsPhysicalColumnNameInOrderby( "scalar HQL queries" ) ) {
-			return;
-		}
-
-		Session s = openSession();
-		s.beginTransaction();
-
-		s.createQuery( "select m.name from Model as m order by m.name" ).list();
-
-		s.createQuery( "select h.name.first from Human h order by h.name.first" ).list();
-
-		s.createQuery( "select h.name from Human h order by h.name" ).list();
-
-		s.getTransaction().commit();
-		s.close();
+		new SyntaxChecker( "from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
+		new SyntaxChecker( "select da.father from DomesticAnimal da join da.owner as o where o.nickName = 'Gavin'" ).checkAll();
 	}
 
-	public void testSelectAndResultColumnReferencesWithCollationAndOrdering() {
-		if ( ! allowsPhysicalColumnNameInHaving( "scalar HQL queries" ) ) {
+	public void testSimpleSelectWithLimitAndOffset() throws Exception {
+		if ( ! ( getDialect().supportsLimit() && getDialect().supportsLimitOffset() ) ) {
+			reportSkip( "dialect does not support offset and limit combo", "limit and offset combination" );
 			return;
 		}
-		if ( ! allowsPhysicalColumnNameInOrderby( "scalar HQL queries" ) ) {
-			return;
-		}
 
-		Session s = openSession();
-		s.beginTransaction();
-
-		s.createQuery( "select m.name, count(*) from Model as m group by m.name having m.name like 'abc%' order by m.name" ).list();
-
-		s.createQuery( "select h.name.first, count(*) from Human h group by h.name.first having count(*) > 1 order by h.name.first" ).list();
-
-		s.createQuery( "select h.name, count(*) from Human h group by h.name having count(*) > 1 order by h.name" ).list();
-
-		s.getTransaction().commit();
-		s.close();
+		// just checking correctness of param binding code...
+		Session session = openSession();
+		session.createQuery( "from Animal" )
+				.setFirstResult( 2 )
+				.setMaxResults( 1 )
+				.list();
+		session.close();
 	}
 
 	public void testJPAPositionalParameterList() {
@@ -403,7 +378,7 @@
 		s.close();
 	}
 
-	public void testFetchInSubqueryFails() {
+	public void testFetchInSubqueryFailureExpected() {
 		Session s = openSession();
 		try {
 			s.createQuery( "from Animal a where a.mother in (select m from Animal a1 inner join a1.mother as m join fetch m.mother)" ).list();
@@ -614,7 +589,7 @@
 		t.commit();
 		s.close();
 	}
-	
+
 	public void testSelectClauseSubselect() {
 		Session s = openSession();
 		Transaction t = s.beginTransaction();
@@ -631,7 +606,7 @@
 		zoo.getAnimals().put("plat123", plat);
 		s.persist( plat );
 		s.persist(zoo);
-		
+
 		s.createQuery("select (select max(z.id) from a.zoo z) from Animal a").list();
 		s.createQuery("select (select max(z.id) from a.zoo z where z.name=:name) from Animal a")
 			.setParameter("name", "Melbourne Zoo").list();
@@ -774,7 +749,7 @@
 		bw.toString();
 		s.delete(a);
 		t.commit();
-		s.close();		
+		s.close();
 	}
 
 	public void testAliases() {
@@ -792,7 +767,7 @@
 		assertEquals(aliases2[1], "avg");
 		s.delete(a);
 		t.commit();
-		s.close();		
+		s.close();
 	}
 
 	public void testParameterMixing() {
@@ -849,9 +824,9 @@
 			.setParameter("country", "Australia")
 			.list();*/
 		t.commit();
-		s.close();		
+		s.close();
 	}
-	
+
 	public void testAggregation() {
 		Session s = openSession();
 		Transaction t = s.beginTransaction();
@@ -871,7 +846,7 @@
 		t.commit();
 		s.close();
 	}
-	
+
 	public void testSelectClauseCase() {
 		Session s = openSession();
 		Transaction t = s.beginTransaction();
@@ -904,28 +879,28 @@
 
 		List list = s.createQuery("from java.lang.Comparable").list();
 		assertEquals( list.size(), 0 );
-		
+
 		list = s.createQuery("from java.lang.Object").list();
 		assertEquals( list.size(), 1 );
-		
+
 		s.delete(product);
-		
+
 		list = s.createQuery("from java.lang.Object").list();
 		assertEquals( list.size(), 0 );
-		
+
 		t.commit();
 		s.close();
 	}
-	
+
 	public void testCoalesce() {
 		Session session = openSession();
 		Transaction txn = session.beginTransaction();
 		session.createQuery("from Human h where coalesce(h.nickName, h.name.first, h.name.last) = 'max'").list();
 		session.createQuery("select nullif(nickName, '1e1') from Human").list();
 		txn.commit();
-		session.close();		
+		session.close();
 	}
-	
+
 	public void testStr() {
 		Session session = openSession();
 		Transaction txn = session.beginTransaction();
@@ -936,14 +911,14 @@
 		if ( getDialect() instanceof DB2Dialect ) {
 			assertTrue( str.startsWith("1.234") );
 		}
-		else if ( getDialect() instanceof SQLServerDialect) {
+		else if ( getDialect() instanceof SQLServerDialect ) {
 			// no assertion as SQLServer always returns nulls here; even trying directly against the
 			// database, it seems to have problems with str() in the where clause...
 		}
 		else {
 			assertTrue( str.startsWith("123.4") );
 		}
-		if ( ! ( getDialect() instanceof SybaseDialect) ) {
+		if ( ! ( getDialect() instanceof SybaseDialect ) ) {
 			// In TransactSQL (the variant spoken by Sybase and SQLServer), the str() function
 			// is explicitly intended for numeric values only...
 			String dateStr1 = (String) session.createQuery("select str(current_date) from Animal").uniqueResult();
@@ -964,7 +939,7 @@
 		txn.commit();
 		session.close();
 	}
-	
+
 	public void testCast() {
 		if ( ( getDialect() instanceof MySQLDialect ) || ( getDialect() instanceof DB2Dialect ) ) {
 			return;
@@ -975,9 +950,9 @@
 		session.createQuery("from Animal a where cast(a.bodyWeight as string) like '1.%'").list();
 		session.createQuery("from Animal a where cast(a.bodyWeight as integer) = 1").list();
 		txn.commit();
-		session.close();		
+		session.close();
 	}
-	
+
 	public void testExtract() {
 		Session session = openSession();
 		Transaction txn = session.beginTransaction();
@@ -988,7 +963,7 @@
 			session.createQuery("select extract(day from m.birthdate), extract(month from m.birthdate), extract(year from m.birthdate) from Mammal m").list();
 		}
 		txn.commit();
-		session.close();		
+		session.close();
 	}
 
 	public void testOneToManyFilter() throws Throwable {
@@ -1020,7 +995,7 @@
 
 		assertEquals( session.createFilter( order.getLineItems(), "" ).list().size(), 1 );
 		assertEquals( session.createFilter( order.getLineItems(), "where this.quantity > :quantity" ).setInteger( "quantity", 5 ).list().size(), 0 );
-		
+
 		session.delete(li);
 		session.delete(order);
 		session.delete(product);
@@ -1058,14 +1033,14 @@
 		assertEquals( session.createFilter( human.getFriends(), "" ).list().size(), 1 );
 		assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight > ?" ).setFloat( 0, 10f ).list().size(), 1 );
 		assertEquals( session.createFilter( human.getFriends(), "where this.bodyWeight < ?" ).setFloat( 0, 10f ).list().size(), 0 );
-		
+
 		session.delete(human);
 		session.delete(friend);
-		
+
 		txn.commit();
 		session.close();
 	}
-	
+
 	public void testSelectExpressions() {
 		createTestBaseData();
 		Session session = openSession();
@@ -1135,7 +1110,7 @@
 		txn.commit();
 		session.close();
 	}
-	
+
 	public void testImplicitJoin() throws Exception {
 		Session session = openSession();
 		Transaction t = session.beginTransaction();
@@ -1298,7 +1273,7 @@
 		destroyTestBaseData();
 
 	}
-	
+
 	public void testStandardFunctions() throws Exception {
 		Session session = openSession();
 		Transaction t = session.beginTransaction();
@@ -1319,7 +1294,7 @@
 		session.delete(p);
 		t.commit();
 		session.close();
-		
+
 	}
 
 	public void testDynamicInstantiationQueries() throws Exception {
@@ -1331,11 +1306,11 @@
 		List results = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).list();
 		assertEquals( "Incorrect result size", 2, results.size() );
 		assertClassAssignability( results.get( 0 ).getClass(), Animal.class );
-		
+
 		Iterator iter = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).iterate();
 		assertTrue( "Incorrect result size", iter.hasNext() );
-		assertTrue( "Incorrect return type", iter.next() instanceof Animal );		
-				
+		assertTrue( "Incorrect return type", iter.next() instanceof Animal );
+
 		results = session.createQuery( "select new list(an.description, an.bodyWeight) from Animal an" ).list();
 		assertEquals( "Incorrect result size", 2, results.size() );
 		assertTrue( "Incorrect return type", results.get( 0 ) instanceof List );
@@ -1351,13 +1326,13 @@
 		Object obj = iter.next();
 		assertTrue( "Incorrect return type", obj instanceof List );
 		assertEquals( "Incorrect return type", ( (List) obj ).size(), 2 );
-		
+
 		iter = ((org.hibernate.classic.Session)session).iterate( "select new list(an.description, an.bodyWeight) from Animal an" );
 		assertTrue( "Incorrect result size", iter.hasNext() );
 		obj = iter.next();
 		assertTrue( "Incorrect return type", obj instanceof List );
 		assertEquals( "Incorrect return type", ( (List) obj ).size(), 2 );
-		
+
 		results = session.createQuery( "select new map(an.description, an.bodyWeight) from Animal an" ).list();
 		assertEquals( "Incorrect result size", 2, results.size() );
 		assertTrue( "Incorrect return type", results.get( 0 ) instanceof Map );
@@ -1377,19 +1352,19 @@
 		obj = iter.next();
 		assertTrue( "Incorrect return type", obj instanceof Map );
 		assertEquals( "Incorrect return type", ( (Map) obj ).size(), 2 );
-		
+
 		ScrollableResults sr = session.createQuery( "select new map(an.description, an.bodyWeight) from Animal an" ).scroll();
 		assertTrue( "Incorrect result size", sr.next() );
 		obj = sr.get(0);
 		assertTrue( "Incorrect return type", obj instanceof Map );
 		assertEquals( "Incorrect return type", ( (Map) obj ).size(), 2 );
 		sr.close();
-		
+
 		sr = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" ).scroll();
 		assertTrue( "Incorrect result size", sr.next() );
 		assertTrue( "Incorrect return type", sr.get(0) instanceof Animal );
 		sr.close();
-						
+
 		// caching...
 		QueryStatistics stats = getSessions().getStatistics().getQueryStatistics( "select new Animal(an.description, an.bodyWeight) from Animal an" );
 		results = session.createQuery( "select new Animal(an.description, an.bodyWeight) from Animal an" )
@@ -1412,28 +1387,28 @@
 
 	public void testIllegalMixedTransformerQueries() {
 		Session session = openSession();
-		
+
 		try {
 			getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
 			fail("'select new' together with a resulttransformer should result in error!");
 		} catch(QueryException he) {
 			assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
 		}
-		
+
 		try {
 			getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).iterate();
 			fail("'select new' together with a resulttransformer should result in error!");
 		} catch(HibernateException he) {
 			assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
 		}
-		
+
 		try {
 			getSelectNewQuery( session ).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).scroll();
 			fail("'select new' together with a resulttransformer should result in error!");
 		} catch(HibernateException he) {
 			assertTrue(he.getMessage().indexOf("ResultTransformer")==0);
 		}
-		
+
 		session.close();
 	}
 
@@ -1447,10 +1422,10 @@
 		String query = "select an.description as description, an.bodyWeight as bodyWeight from Animal an order by bodyWeight desc";
 
 		Session session = openSession();
-		
+
 		List results = session.createQuery( query )
 		.setResultTransformer(Transformers.aliasToBean(Animal.class)).list();
-		assertEquals( "Incorrect result size", results.size(), 2 );		
+		assertEquals( "Incorrect result size", results.size(), 2 );
 		assertTrue( "Incorrect return type", results.get(0) instanceof Animal );
 		Animal firstAnimal = (Animal) results.get(0);
 		Animal secondAnimal = (Animal) results.get(1);
@@ -1458,41 +1433,41 @@
 		assertEquals("Mammal #2", secondAnimal.getDescription());
 		assertFalse(session.contains(firstAnimal));
 		session.close();
-		
+
 		session = openSession();
-		
+
 		Iterator iter = session.createQuery( query )
 	     .setResultTransformer(Transformers.aliasToBean(Animal.class)).iterate();
 		assertTrue( "Incorrect result size", iter.hasNext() );
-		assertTrue( "Incorrect return type", iter.next() instanceof Animal );				
+		assertTrue( "Incorrect return type", iter.next() instanceof Animal );
 
 		session.close();
-		
+
 		session = openSession();
-		
+
 		ScrollableResults sr = session.createQuery( query )
 	     .setResultTransformer(Transformers.aliasToBean(Animal.class)).scroll();
 		assertTrue( "Incorrect result size", sr.next() );
 		assertTrue( "Incorrect return type", sr.get(0) instanceof Animal );
 		assertFalse(session.contains(sr.get(0)));
 		sr.close();
-		
+
 		session.close();
-		
+
 		session = openSession();
 
 		results = session.createQuery( "select a from Animal a, Animal b order by a.id" )
 				.setResultTransformer(new DistinctRootEntityResultTransformer())
 				.list();
-		assertEquals( "Incorrect result size", 2, results.size());		
+		assertEquals( "Incorrect result size", 2, results.size());
 		assertTrue( "Incorrect return type", results.get(0) instanceof Animal );
 		firstAnimal = (Animal) results.get(0);
 		secondAnimal = (Animal) results.get(1);
 		assertEquals("Mammal #1", firstAnimal.getDescription());
 		assertEquals("Mammal #2", secondAnimal.getDescription());
-		
+
 		session.close();
-		
+
 		destroyTestBaseData();
 	}
 
@@ -1503,10 +1478,10 @@
 		String query = "select an as an from Animal an order by bodyWeight desc";
 
 		Session session = openSession();
-		
+
 		List results = session.createQuery( query )
 		.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
-		assertEquals( "Incorrect result size", results.size(), 2 );		
+		assertEquals( "Incorrect result size", results.size(), 2 );
 		assertTrue( "Incorrect return type", results.get(0) instanceof Map );
 		Map map = ((Map) results.get(0));
 		assertEquals(1, map.size());
@@ -1518,9 +1493,9 @@
 		assertTrue(session.contains(firstAnimal));
 		assertSame(firstAnimal, session.get(Animal.class,firstAnimal.getId()));
 		session.close();
-		
+
 		session = openSession();
-		
+
 		Iterator iter = session.createQuery( query )
 	     .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).iterate();
 		assertTrue( "Incorrect result size", iter.hasNext() );
@@ -1528,20 +1503,20 @@
 		firstAnimal = (Animal) map.get("an");
 		assertEquals("Mammal #1", firstAnimal.getDescription());
 		assertTrue( "Incorrect result size", iter.hasNext() );
-		
+
 		session.close();
-		
+
 		session = openSession();
-		
+
 		ScrollableResults sr = session.createQuery( query )
 	     .setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).scroll();
 		assertTrue( "Incorrect result size", sr.next() );
 		assertTrue( "Incorrect return type", sr.get(0) instanceof Map );
 		assertFalse(session.contains(sr.get(0)));
 		sr.close();
-		
+
 		session.close();
-						
+
 		destroyTestBaseData();
 	}
 
@@ -1574,7 +1549,7 @@
 
 		hql = "select length(a.description) from Animal a";
 		session.createQuery(hql).list();
-		
+
 		//note: postgres and db2 don't have a 3-arg form, it gets transformed to 2-args
 		hql = "from Animal a where locate('abc', a.description, 2) = 2";
 		session.createQuery(hql).list();
@@ -1584,7 +1559,7 @@
 
 		hql = "select locate('cat', a.description, 2) from Animal a";
 		session.createQuery(hql).list();
-		
+
 		if ( !( getDialect() instanceof DB2Dialect ) ) {
 			hql = "from Animal a where trim(trailing '_' from a.description) = 'cat'";
 			session.createQuery(hql).list();
@@ -1632,20 +1607,20 @@
 
 		hql = "from Animal a where a.description like 'x%ax%' escape 'x'";
 		session.createQuery(hql).list();
-		
+
 		session.close();
 	}
 
 	public void testSubselectBetween() {
 		if(supportsSubselectOnLeftSideIn()) {
 			assertResultSize("from Animal x where (select max(a.bodyWeight) from Animal a) in (1,2,3)", 0);
-		}		
+		}
 		assertResultSize("from Animal x where (select max(a.bodyWeight) from Animal a) between 0 and 100", 0);
 		assertResultSize("from Animal x where (select max(a.description) from Animal a) like 'big%'", 0);
 		assertResultSize("from Animal x where (select max(a.bodyWeight) from Animal a) is not null", 0);
 		assertResultSize("from Animal x where exists (select max(a.bodyWeight) from Animal a)", 0);
 	}
-	
+
 	private void assertResultSize(String hql, int size) {
 		Session session = openSession();
 		Transaction txn = session.beginTransaction();
@@ -1654,4 +1629,65 @@
 		session.close();
 	}
 
+	private interface QueryPreparer {
+		public void prepare(Query query);
+	}
+
+	private static final QueryPreparer DEFAULT_PREPARER = new QueryPreparer() {
+		public void prepare(Query query) {
+		}
+	};
+
+	private class SyntaxChecker {
+		private final String hql;
+		private final QueryPreparer preparer;
+
+		public SyntaxChecker(String hql) {
+			this( hql, DEFAULT_PREPARER );
+		}
+
+		public SyntaxChecker(String hql, QueryPreparer preparer) {
+			this.hql = hql;
+			this.preparer = preparer;
+		}
+
+		public void checkAll() {
+			checkList();
+			checkIterate();
+			checkScroll();
+		}
+
+		public SyntaxChecker checkList() {
+			Session s = openSession();
+			s.beginTransaction();
+			Query query = s.createQuery( hql );
+			preparer.prepare( query );
+			query.list();
+			s.getTransaction().commit();
+			s.close();
+			return this;
+		}
+
+		public SyntaxChecker checkScroll() {
+			Session s = openSession();
+			s.beginTransaction();
+			Query query = s.createQuery( hql );
+			preparer.prepare( query );
+			query.scroll();
+			s.getTransaction().commit();
+			s.close();
+			return this;
+		}
+
+		public SyntaxChecker checkIterate() {
+			Session s = openSession();
+			s.beginTransaction();
+			Query query = s.createQuery( hql );
+			preparer.prepare( query );
+			query.iterate();
+			s.getTransaction().commit();
+			s.close();
+			return this;
+		}
+	}
 }

Modified: trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java	2006-11-16 17:33:12 UTC (rev 10823)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java	2006-11-16 19:32:48 UTC (rev 10824)
@@ -3,39 +3,37 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
+import antlr.RecognitionException;
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
-import org.hibernate.classic.Session;
-import org.hibernate.criterion.Projections;
+import org.hibernate.Hibernate;
 import org.hibernate.dialect.DB2Dialect;
 import org.hibernate.dialect.HSQLDialect;
 import org.hibernate.dialect.MySQLDialect;
 import org.hibernate.dialect.Oracle9Dialect;
 import org.hibernate.dialect.PostgreSQLDialect;
 import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.query.HQLQueryPlan;
+import org.hibernate.engine.query.ReturnMetadata;
+import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.QueryTranslatorFactory;
+import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
 import org.hibernate.hql.ast.DetailedSemanticException;
 import org.hibernate.hql.ast.QuerySyntaxException;
-import org.hibernate.hql.ast.ASTQueryTranslatorFactory;
 import org.hibernate.hql.ast.QueryTranslatorImpl;
 import org.hibernate.hql.ast.tree.ConstructorNode;
 import org.hibernate.hql.ast.tree.DotNode;
 import org.hibernate.hql.ast.tree.IndexNode;
 import org.hibernate.hql.ast.tree.SelectClause;
-import org.hibernate.hql.QueryTranslatorFactory;
-import org.hibernate.hql.QueryTranslator;
-import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.engine.query.HQLQueryPlan;
-import org.hibernate.engine.query.ReturnMetadata;
-import org.hibernate.Criteria;
-import org.hibernate.Hibernate;
 
-import antlr.RecognitionException;
-
 /**
  * Tests cases where the AST based query translator and the 'classic' query translator generate identical SQL.
  *
@@ -165,7 +163,7 @@
 		assertTranslation("from Animal x where exists (select max(a.bodyWeight) from Animal a)");
 		assertTranslation("from Animal x where (select max(a.bodyWeight) from Animal a) in (1,2,3)");
 	}
-	
+
 	public void testFetchOrderBy() {
 		assertTranslation("from Animal a left outer join fetch a.offspring where a.mother.id = :mid order by a.description");
 	}
@@ -176,7 +174,7 @@
 		assertTranslation("from Animal a join fetch a.offspring o order by o.description");
 		assertTranslation("from Animal a join a.offspring o order by a.description, o.description");
 	}
-	
+
 	public void testExpressionWithParamInFunction() {
 		assertTranslation("from Animal a where abs(a.bodyWeight-:param) < 2.0");
 		assertTranslation("from Animal a where abs(:param - a.bodyWeight) < 2.0");
@@ -188,7 +186,7 @@
 		if ( getDialect() instanceof PostgreSQLDialect ) return;
 		assertTranslation("from Animal where abs(cast(1 as float) - cast(:param as float)) = 1.0");
 	}
-	
+
 	public void testCompositeKeysWithPropertyNamedId() {
 		assertTranslation( "select e.id.id from EntityWithCrazyCompositeKey e" );
 		assertTranslation( "select max(e.id.id) from EntityWithCrazyCompositeKey e" );
@@ -317,7 +315,7 @@
 		        "select o.mother.bodyWeight, count(distinct o) " +
 		        "from Animal an " +
 		        "   join an.offspring as o " +
-		        "group by o.mother.bodyWeight" 
+		        "group by o.mother.bodyWeight"
 		);
 	}
 
@@ -335,14 +333,14 @@
 
 		DotNode.useThetaStyleImplicitJoins = false;
 	}
-	
+
 	public void testSizeFunctionAndProperty() {
 		assertTranslation("from Animal a where a.offspring.size > 0");
 		assertTranslation("from Animal a join a.offspring where a.offspring.size > 1");
 		assertTranslation("from Animal a where size(a.offspring) > 0");
 		assertTranslation("from Animal a join a.offspring o where size(a.offspring) > 1");
 		assertTranslation("from Animal a where size(a.offspring) > 1 and size(a.offspring) < 100");
-		
+
 		assertTranslation("from Human a where a.family.size > 0");
 		assertTranslation("from Human a join a.family where a.family.size > 1");
 		assertTranslation("from Human a where size(a.family) > 0");
@@ -424,7 +422,7 @@
 		assertTranslation("select minelement(zoo.animals) from Zoo zoo");
 		assertTranslation("select elements(zoo.animals) from Zoo zoo");
 	}*/
-	
+
 	public void testFetchCollectionOfValues() throws Exception {
 		assertTranslation( "from Baz baz left join fetch baz.stringSet" );
 	}
@@ -447,7 +445,7 @@
 		assertTranslation( "from User u where 'read' in elements(u.permissions)" );
 		assertTranslation( "from User u where 'write' <> all elements(u.permissions)" );
 	}
-	
+
 	/*public void testManyToManyElementFunctionInSelect() throws Exception {
 		assertTranslation("select maxelement(human.friends) from Human human");
 		assertTranslation("select elements(human.friends) from Human human");
@@ -507,7 +505,7 @@
 		// 2004-06-27 [jsd] Recognize 'is null' properly.  Generate 'and' and 'or' as well.
 		assertTranslation( "from Animal an where (an.bodyWeight > 10 and an.bodyWeight < 100) or an.bodyWeight is null" );
 	}
-	
+
 	public void testEscapedQuote() throws Exception {
 		assertTranslation( "from Human h where h.nickName='1 ov''tha''few'");
 	}
@@ -515,7 +513,7 @@
 	public void testCaseWhenElse() {
 		assertTranslation( "from Human h where case when h.nickName='1ovthafew' then 'Gavin' when h.nickName='turin' then 'Christian' else h.nickName end = h.name.first" );
 	}
-	
+
 	public void testCaseExprWhenElse() {
 		assertTranslation( "from Human h where case h.nickName when '1ovthafew' then 'Gavin' when 'turin' then 'Christian' else h.nickName end = h.name.first" );
 	}
@@ -628,7 +626,7 @@
 		assertTranslation( "select count(*) from Human h group by year(sysdate)" );
 	}
 
-	
+
 	public void testPolymorphism() throws Exception {
 		HashMap replacements = new HashMap();
 		replacements.put( "false", "0" );
@@ -723,7 +721,7 @@
 
     /**
      * Test for HHH-559
-     */ 
+     */
     public void testMultibyteCharacterConstant() throws Exception {
         assertTranslation( "from Zoo zoo join zoo.animals an where an.description like '%\u4e2d%'" );
     }
@@ -813,8 +811,8 @@
 		assertTranslation( "select count(distinct an) from Animal an" );
 		assertTranslation( "select count(distinct an.id) from Animal an" );
 		assertTranslation( "select count(all an.id) from Animal an" );
-	}	
-		
+	}
+
 	public void testSelectProperty() throws Exception {
 		assertTranslation( "select an.bodyWeight, mo.bodyWeight from Animal an inner join an.mother mo where an.bodyWeight < mo.bodyWeight" );
 	}
@@ -939,9 +937,19 @@
 	}
 
 	public void testClassName() throws Exception {
+		// The Zoo reference is OK; Zoo is discriminator-based;
+		// the old parser could handle these correctly
+		//
+		// However, the Animal one ares not; Animal is joined subclassing;
+		// the old parser does not handle thee correctly.  The new parser
+		// previously did not handle them correctly in that same way.  So they
+		// used to pass regression even though the output was bogus SQL...
+		//
+		// I have moved the Animal ones (plus duplicating the Zoo one)
+		// to ASTParserLoadingTest for syntax checking.
 		assertTranslation( "from Zoo zoo where zoo.class = PettingZoo" );
-		assertTranslation( "from DomesticAnimal an where an.class = Dog" );
-		assertTranslation( "from Animal an where an.class = Dog" );
+//		assertTranslation( "from DomesticAnimal an where an.class = Dog" );
+//		assertTranslation( "from Animal an where an.class = Dog" );
 	}
 
 	public void testSelectDialectFunction() throws Exception {
@@ -1008,11 +1016,6 @@
 		assertTranslation( "from Baz baz inner join baz.components comp where comp.name='foo'" );
 	}
 
-	public void testNestedComponentIsNull() {
-		// From MapTest...
-		assertTranslation( "from Commento c where c.marelo.commento.mcompr is null" );
-	}
-
 	public void testOneToOneJoinedFetch() throws Exception {
 		// From OneToOneTest.testOneToOneOnSubclass
 		assertTranslation( "from org.hibernate.test.onetoone.joined.Person p join fetch p.address left join fetch p.mailingAddress" );
@@ -1131,7 +1134,7 @@
 		assertTranslation( "from Animal a where a in (select o from Animal an join an.offspring o)" );
 		DotNode.useThetaStyleImplicitJoins = false;
 	}
-	
+
 	public void testJoinedSubclassImplicitJoin() throws Exception {
 		// From MultiTableTest.testQueries()
 		// TODO: This produces the proper from clause now, but the parens in the where clause are different.
@@ -1162,13 +1165,13 @@
 	public void testOneToManyElementFunctionInWhere() throws Exception {
 		assertTranslation( "from Zoo zoo where 'dog' in indices(zoo.mammals)" );
 		assertTranslation( "from Zoo zoo, Dog dog where dog in elements(zoo.mammals)" );
-	}	
+	}
 
 	/*public void testManyToManyElementFunctionInSelect() throws Exception {
 		assertTranslation("select elements(zoo.mammals) from Zoo zoo");
 		assertTranslation("select indices(zoo.mammals) from Zoo zoo");
 	}*/
-	
+
 	public void testManyToManyInJoin() throws Exception {
 		assertTranslation( "select x.id from Human h1 join h1.family x" );
 		//assertTranslation("select index(h2) from Human h1 join h1.family h2");
@@ -1189,8 +1192,8 @@
 		assertTranslation( "from Zoo zoo join zoo.mammals m" );
 		assertTranslation( "from Zoo" );
 		assertTranslation( "from Zoo zoo join zoo.mammals" );
-	}	
-	
+	}
+
 	public void testVectorSubselect() {
 		assertTranslation( "from Animal a where ('foo', 'bar') in (select m.description, m.bodyWeight from a.mother m)" );
 	}
@@ -1279,5 +1282,5 @@
 	public static Test suite() {
         return new TestSuite( HQLTest.class );
     }
-	
+
 }




More information about the hibernate-commits mailing list