Author: ste...@jb... Date: 2006-06-27 22:53:39 -0400 (Tue, 27 Jun 2006) New Revision: 10060 Added: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BooleanLiteralNode.java trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.java Modified: trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java trunk/Hibernate3/src/org/hibernate/dialect/MySQLDialect.java trunk/Hibernate3/src/org/hibernate/dialect/PostgreSQLDialect.java trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java trunk/Hibernate3/src/org/hibernate/hql/ast/SqlASTFactory.java trunk/Hibernate3/src/org/hibernate/hql/ast/SqlGenerator.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/JavaConstantNode.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/LiteralNode.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/Node.java trunk/Hibernate3/src/org/hibernate/hql/ast/tree/ParameterNode.java trunk/Hibernate3/src/org/hibernate/impl/AbstractQueryImpl.java trunk/Hibernate3/test/org/hibernate/test/component/ComponentTest.java trunk/Hibernate3/test/org/hibernate/test/component/User.hbm.xml trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java trunk/Hibernate3/test/org/hibernate/test/hql/BulkManipulationTest.java trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java Log: HHH-1855 : HQL and boolean handling (literals and params); HHH-1858 : HQL and many-to-any; HHH-1861 : HQL and components Modified: trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -1060,4 +1060,20 @@ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) { return new SelectLockingStrategy( lockable, lockMode ); } + + /** + * Is this dialect known to support what ANSI-SQL terms "row value + * constructor" syntax; sometimes called tuple syntax. + * <p/> + * Basically, does it support syntax like + * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...". + * + * @return True if this SQL dialect is known to support "row value + * constructor" syntax; false otherwise. + * @since 3.2 + */ + public boolean supportsRowValueConstructorSyntax() { + // return false here, as most databases do not properly support this construct... + return false; + } } Modified: trunk/Hibernate3/src/org/hibernate/dialect/MySQLDialect.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/MySQLDialect.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/dialect/MySQLDialect.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -320,5 +320,8 @@ ResultSet rs = ps.getResultSet(); return rs; } - + + public boolean supportsRowValueConstructorSyntax() { + return true; + } } \ No newline at end of file Modified: trunk/Hibernate3/src/org/hibernate/dialect/PostgreSQLDialect.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/dialect/PostgreSQLDialect.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/dialect/PostgreSQLDialect.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -293,4 +293,7 @@ } }; + public boolean supportsRowValueConstructorSyntax() { + return true; + } } Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -45,8 +45,6 @@ import org.hibernate.hql.ast.tree.UpdateStatement; import org.hibernate.hql.ast.tree.Node; import org.hibernate.hql.ast.tree.OperatorNode; -import org.hibernate.hql.ast.tree.BinaryLogicOperatorNode; -import org.hibernate.hql.ast.tree.InLogicOperatorNode; import org.hibernate.hql.ast.util.ASTPrinter; import org.hibernate.hql.ast.util.ASTUtil; import org.hibernate.hql.ast.util.AliasGenerator; @@ -923,6 +921,7 @@ } protected void evaluateAssignment(AST eq) throws SemanticException { + prepareLogicOperator( eq ); Queryable persister = getCurrentFromClause().getFromElement().getQueryable(); evaluateAssignment( eq, persister, -1 ); } Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/SqlASTFactory.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/SqlASTFactory.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/SqlASTFactory.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -39,6 +39,7 @@ import org.hibernate.hql.ast.tree.InLogicOperatorNode; import org.hibernate.hql.ast.tree.JavaConstantNode; import org.hibernate.hql.ast.tree.SessionFactoryAwareNode; +import org.hibernate.hql.ast.tree.BooleanLiteralNode; import java.lang.reflect.Constructor; @@ -117,9 +118,10 @@ case NUM_LONG: case NUM_DOUBLE: case QUOTED_STRING: + return LiteralNode.class; case TRUE: case FALSE: - return LiteralNode.class; + return BooleanLiteralNode.class; case JAVA_CONSTANT: return JavaConstantNode.class; case ORDER: Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/SqlGenerator.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/SqlGenerator.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/SqlGenerator.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -13,13 +13,14 @@ import org.hibernate.hql.antlr.SqlGeneratorBase; import org.hibernate.hql.ast.tree.MethodNode; import org.hibernate.hql.ast.tree.FromElement; -import org.hibernate.hql.ast.tree.JavaConstantNode; +import org.hibernate.hql.ast.tree.Node; /** * Generates SQL by overriding callback methods in the base class, which does * the actual SQL AST walking. * - * @author Joshua + * @author Joshua Davis + * @author Steve Ebersole */ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter { /** @@ -45,8 +46,8 @@ } protected void out(AST n) { - if ( n instanceof JavaConstantNode ) { - out( ( ( JavaConstantNode ) n ).getSqlText() ); + if ( n instanceof Node ) { + out( ( ( Node ) n ).getRenderText( sessionFactory ) ); } else { super.out( n ); Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -2,14 +2,21 @@ import org.hibernate.type.Type; import org.hibernate.Hibernate; +import org.hibernate.TypeMismatchException; +import org.hibernate.HibernateException; +import org.hibernate.util.StringHelper; +import org.hibernate.hql.antlr.HqlSqlTokenTypes; +import org.hibernate.dialect.HSQLDialect; +import org.hibernate.engine.SessionFactoryImplementor; import antlr.SemanticException; +import antlr.collections.AST; /** * Contract for nodes representing binary operators. * * @author Steve Ebersole */ -public class BinaryLogicOperatorNode extends SqlNode implements BinaryOperatorNode { +public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryOperatorNode { /** * Performs the operator node initialization by seeking out any parameter * nodes and setting their expected type, if possible. @@ -23,16 +30,149 @@ if ( rhs == null ) { throw new SemanticException( "right-hand operand of a binary operator was null" ); } - if ( ExpectedTypeAwareNode.class.isAssignableFrom( lhs.getClass() ) - && SqlNode.class.isAssignableFrom( rhs.getClass() ) ) { - ( ( ExpectedTypeAwareNode ) lhs ).setExpectedType( ( ( SqlNode ) rhs ).getDataType() ); + + Type lhsType = extractDataType( lhs ); + Type rhsType = extractDataType( rhs ); + + if ( lhsType == null ) { + lhsType = rhsType; } - else if ( ExpectedTypeAwareNode.class.isAssignableFrom( rhs.getClass() ) - && SqlNode.class.isAssignableFrom( lhs.getClass() ) ) { - ( ( ExpectedTypeAwareNode ) rhs ).setExpectedType( ( ( SqlNode ) lhs ).getDataType() ); + if ( rhsType == null ) { + rhsType = lhsType; } + + if ( ExpectedTypeAwareNode.class.isAssignableFrom( lhs.getClass() ) ) { + ( ( ExpectedTypeAwareNode ) lhs ).setExpectedType( rhsType ); + } + if ( ExpectedTypeAwareNode.class.isAssignableFrom( rhs.getClass() ) ) { + ( ( ExpectedTypeAwareNode ) rhs ).setExpectedType( lhsType ); + } + + mutateRowValueConstructorSyntaxesIfNecessary( lhsType, rhsType ); } + protected final void mutateRowValueConstructorSyntaxesIfNecessary(Type lhsType, Type rhsType) { + // TODO : this really needs to be delayed unitl after we definitively know all node types + // where this is currently a problem is parameters for which where we cannot unequivocally + // resolve an expected type + SessionFactoryImplementor sessionFactory = getSessionFactoryHelper().getFactory(); + if ( lhsType != null && rhsType != null ) { + int lhsColumnSpan = lhsType.getColumnSpan( sessionFactory ); + if ( lhsColumnSpan != rhsType.getColumnSpan( sessionFactory ) ) { + throw new TypeMismatchException( + "left and right hand sides of a binary logic operator were incompatibile [" + + lhsType.getName() + " : "+ rhsType.getName() + "]" + ); + } + if ( lhsColumnSpan > 1 ) { + // for dialects which are known to not support ANSI-SQL row-value-constructor syntax, + // we should mutate the tree. + if ( !sessionFactory.getDialect().supportsRowValueConstructorSyntax() ) { + mutateRowValueConstructorSyntax( lhsColumnSpan ); + } + } + } + } + + /** + * Mutate the subtree relating to a row-value-constructor to instead use + * a series of ANDed predicates. This allows multi-column type comparisons + * and explicit row-value-constructor syntax even on databases which do + * not support row-value-constructor. + * <p/> + * For example, here we'd mutate "... where (col1, col2) = ('val1', 'val2) ..." to + * "... where col1 = 'val1' and col2 = 'val2' ..." + * + * @param valueElements The number of elements in the row value constructor list. + */ + private void mutateRowValueConstructorSyntax(int valueElements) { + // mutation depends on the types of nodes invloved... + int comparisonType = getType(); + String comparisonText = getText(); + setType( HqlSqlTokenTypes.AND ); + setText( "AND" ); + String[] lhsElementTexts = extractMutationTexts( getLeftHandOperand(), valueElements ); + String[] rhsElementTexts = extractMutationTexts( getRightHandOperand(), valueElements ); + + AST container = this; + for ( int i = valueElements - 1; i > 0; i-- ) { + + if ( i == 1 ) { + AST op1 = getASTFactory().create( comparisonType, comparisonText ); + AST lhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[0] ); + AST rhs1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[0] ); + op1.setFirstChild( lhs1 ); + lhs1.setNextSibling( rhs1 ); + container.setFirstChild( op1 ); + AST op2 = getASTFactory().create( comparisonType, comparisonText ); + AST lhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[1] ); + AST rhs2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[1] ); + op2.setFirstChild( lhs2 ); + lhs2.setNextSibling( rhs2 ); + op1.setNextSibling( op2 ); + } + else { + AST op = getASTFactory().create( comparisonType, comparisonText ); + AST lhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, lhsElementTexts[i] ); + AST rhs = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, rhsElementTexts[i] ); + op.setFirstChild( lhs ); + lhs.setNextSibling( rhs ); + AST newContainer = getASTFactory().create( HqlSqlTokenTypes.AND, "AND" ); + container.setFirstChild( newContainer ); + newContainer.setNextSibling( op ); + container = newContainer; + } + } + } + + private static String[] extractMutationTexts(Node operand, int count) { + if ( operand instanceof ParameterNode ) { + String[] rtn = new String[count]; + for ( int i = 0; i < count; i++ ) { + rtn[i] = "?"; + } + return rtn; + } + else if ( operand.getType() == HqlSqlTokenTypes.VECTOR_EXPR ) { + String[] rtn = new String[ operand.getNumberOfChildren() ]; + int x = 0; + AST node = operand.getFirstChild(); + while ( node != null ) { + rtn[ x++ ] = node.getText(); + node = node.getNextSibling(); + } + return rtn; + } + else if ( operand instanceof SqlNode ) { + String nodeText = operand.getText(); + if ( nodeText.startsWith( "(" ) ) { + nodeText = nodeText.substring( 1 ); + } + if ( nodeText.endsWith( ")" ) ) { + nodeText = nodeText.substring( 0, nodeText.length() - 1 ); + } + String[] splits = StringHelper.split( ", ", nodeText ); + if ( count != splits.length ) { + throw new HibernateException( "SqlNode's text did not reference expected number of columns" ); + } + return splits; + } + else { + throw new HibernateException( "dont know how to extract row value elements from node : " + operand ); + } + } + + protected Type extractDataType(Node operand) { + Type type = null; + if ( operand instanceof SqlNode ) { + type = ( ( SqlNode ) operand ).getDataType(); + } + if ( type == null && operand instanceof ExpectedTypeAwareNode ) { + type = ( ( ExpectedTypeAwareNode ) operand ).getExpectedType(); + } + return type; + } + public Type getDataType() { // logic operators by definition resolve to booleans return Hibernate.BOOLEAN; Added: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BooleanLiteralNode.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BooleanLiteralNode.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/BooleanLiteralNode.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -0,0 +1,50 @@ +package org.hibernate.hql.ast.tree; + +import org.hibernate.type.Type; +import org.hibernate.type.BooleanType; +import org.hibernate.Hibernate; +import org.hibernate.QueryException; +import org.hibernate.engine.SessionFactoryImplementor; + +/** + * Represents a boolean literal within a query. + * + * @author Steve Ebersole + */ +public class BooleanLiteralNode extends LiteralNode implements ExpectedTypeAwareNode { + private Type expectedType; + + public Type getDataType() { + return expectedType == null ? Hibernate.BOOLEAN : expectedType; + } + + public BooleanType getTypeInternal() { + return ( BooleanType ) getDataType(); + } + + public Boolean getValue() { + return getType() == TRUE ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Expected-types really only pertinent here for boolean literals... + * + * @param expectedType + */ + public void setExpectedType(Type expectedType) { + this.expectedType = expectedType; + } + + public Type getExpectedType() { + return expectedType; + } + + public String getRenderText(SessionFactoryImplementor sessionFactory) { + try { + return getTypeInternal().objectToSQLString( getValue(), sessionFactory.getDialect() ); + } + catch( Throwable t ) { + throw new QueryException( "Unable to render boolean literal value", t ); + } + } +} Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/FromElementType.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -381,7 +381,14 @@ } return collectionPropertyMapping; } - if ( queryableCollection.getElementType().isComponentType() ) { // Collection of components. + if ( queryableCollection.getElementType().isAnyType() ) { + // collection of <many-to-any/> mappings... + // used to circumvent the component-collection check below... + return queryableCollection; + + } + if ( queryableCollection.getElementType().isComponentType() ) { + // Collection of components. if ( propertyName.equals( EntityPersister.ENTITY_ID ) ) { return ( PropertyMapping ) queryableCollection.getOwnerEntityPersister(); } Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/JavaConstantNode.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/JavaConstantNode.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/JavaConstantNode.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -48,11 +48,6 @@ this.factory = factory; } - public String getSqlText() { - Type type = expectedType == null ? heuristicType : expectedType; - return resolveToLiteralString( type ); - } - private String resolveToLiteralString(Type type) { try { LiteralType literalType = ( LiteralType ) type; @@ -63,4 +58,9 @@ throw new QueryException( QueryTranslator.ERROR_CANNOT_FORMAT_LITERAL + constantExpression, t ); } } + + public String getRenderText(SessionFactoryImplementor sessionFactory) { + Type type = expectedType == null ? heuristicType : expectedType; + return resolveToLiteralString( type ); + } } Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/LiteralNode.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/LiteralNode.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/LiteralNode.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -38,5 +38,4 @@ return null; } } - } Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/Node.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/Node.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/Node.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -3,12 +3,13 @@ import antlr.collections.AST; import antlr.Token; import org.hibernate.util.StringHelper; +import org.hibernate.engine.SessionFactoryImplementor; /** - * Generic AST Node. - * User: Joshua Davis<br> - * Date: Sep 23, 2005<br> - * Time: 12:20:53 PM<br> + * Base node class for use by Hibernate within its AST trees. + * + * @author Joshua Davis + * @author Steve Ebersole */ public class Node extends antlr.CommonAST { private String filename; @@ -24,6 +25,17 @@ super(tok); // This will call initialize(tok)! } + /** + * Retrieve the text to be used for rendering this particular node. + * + * @param sessionFactory The session factory + * @return The text to use for rendering + */ + public String getRenderText(SessionFactoryImplementor sessionFactory) { + // The basic implementation is to simply use the node's text + return getText(); + } + public void initialize(Token tok) { super.initialize(tok); filename = tok.getFilename(); @@ -34,9 +46,8 @@ } public void initialize(AST t) { - super.initialize(t); - if (t instanceof Node) - { + super.initialize( t ); + if ( t instanceof Node ) { Node n = (Node)t; filename = n.filename; line = n.line; Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/ParameterNode.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/ParameterNode.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/ParameterNode.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -3,6 +3,7 @@ import org.hibernate.param.ParameterSpecification; import org.hibernate.type.Type; +import org.hibernate.engine.SessionFactoryImplementor; /** * Implementation of ParameterNode. @@ -32,4 +33,20 @@ public Type getExpectedType() { return getHqlParameterSpecification() == null ? null : getHqlParameterSpecification().getExpectedType(); } + + public String getRenderText(SessionFactoryImplementor sessionFactory) { + int count = 0; + if ( getExpectedType() != null && ( count = getExpectedType().getColumnSpan( sessionFactory ) ) > 1 ) { + StringBuffer buffer = new StringBuffer(); + buffer.append( "(?" ); + for ( int i = 1; i < count; i++ ) { + buffer.append( ", ?" ); + } + buffer.append( ")" ); + return buffer.toString(); + } + else { + return "?"; + } + } } Modified: trunk/Hibernate3/src/org/hibernate/impl/AbstractQueryImpl.java =================================================================== --- trunk/Hibernate3/src/org/hibernate/impl/AbstractQueryImpl.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/src/org/hibernate/impl/AbstractQueryImpl.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -385,6 +385,14 @@ return this; } + protected Type determineType(int paramPosition, Object paramValue, Type defaultType) { + Type type = parameterMetadata.getOrdinalParameterExpectedType( paramPosition + 1 ); + if ( type == null ) { + type = defaultType; + } + return type; + } + protected Type determineType(int paramPosition, Object paramValue) throws HibernateException { Type type = parameterMetadata.getOrdinalParameterExpectedType( paramPosition + 1 ); if ( type == null ) { @@ -393,6 +401,14 @@ return type; } + protected Type determineType(String paramName, Object paramValue, Type defaultType) { + Type type = parameterMetadata.getNamedParameterExpectedType( paramName ); + if ( type == null ) { + type = defaultType; + } + return type; + } + protected Type determineType(String paramName, Object paramValue) throws HibernateException { Type type = parameterMetadata.getNamedParameterExpectedType( paramName ); if ( type == null ) { @@ -448,7 +464,9 @@ } public Query setBoolean(int position, boolean val) { - setParameter(position, val ? Boolean.TRUE : Boolean.FALSE, Hibernate.BOOLEAN); + Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE; + Type typeToUse = determineType( position, valueToUse, Hibernate.BOOLEAN ); + setParameter( position, valueToUse, typeToUse ); return this; } @@ -543,7 +561,9 @@ } public Query setBoolean(String name, boolean val) { - setParameter(name, val ? Boolean.TRUE : Boolean.FALSE, Hibernate.BOOLEAN); + Boolean valueToUse = val ? Boolean.TRUE : Boolean.FALSE; + Type typeToUse = determineType( name, valueToUse, Hibernate.BOOLEAN ); + setParameter( name, valueToUse, typeToUse ); return this; } Modified: trunk/Hibernate3/test/org/hibernate/test/component/ComponentTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/component/ComponentTest.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/component/ComponentTest.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -14,6 +14,7 @@ import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.Oracle9Dialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.test.TestCase; /** @@ -89,6 +90,28 @@ t.commit(); s.close(); } + + public void testComponentQueries() { +// if ( !dialectSupportsRowValueConstructorSyntax() ) { +// return; +// } + Session s = openSession(); + Transaction t = s.beginTransaction(); + Employee emp = new Employee(); + emp.setHireDate( new Date() ); + emp.setPerson( new Person() ); + emp.getPerson().setName( "steve" ); + emp.getPerson().setDob( new Date() ); + s.save( emp ); + + s.createQuery( "from Employee e where e.person = :p and 1 = 1 and 2=2" ).setParameter( "p", emp.getPerson() ).list(); + s.createQuery( "from Employee e where :p = e.person" ).setParameter( "p", emp.getPerson() ).list(); + s.createQuery( "from Employee e where e.person = ('steve', current_timestamp)" ).list(); + + s.delete( emp ); + t.commit(); + s.close(); + } public void testComponentFormulaQuery() { @@ -110,7 +133,7 @@ t.commit(); s.close(); } - + public void testNamedQuery() { if ( getDialect() instanceof PostgreSQLDialect ) return; //postgres got no year() function if ( getDialect() instanceof Oracle9Dialect ) return; //oracle got no year() function Modified: trunk/Hibernate3/test/org/hibernate/test/component/User.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/component/User.hbm.xml 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/component/User.hbm.xml 2006-06-28 02:53:39 UTC (rev 10060) @@ -25,6 +25,17 @@ update="false"/> </component> </class> + + <class name="Employee" table="T_EMP"> + <id name="id" type="long" column="ID"> + <generator class="increment"/> + </id> + <property name="hireDate" type="date" column="HIRE_DATE"/> + <component name="person"> + <property name="name" update="false" not-null="true"/> + <property name="dob" update="false" not-null="true"/> + </component> + </class> <query name="userNameIn"><![CDATA[from User where person.name in (:nameList) or userName in (:nameList)]]></query> Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -31,6 +31,12 @@ import org.hibernate.dialect.Oracle9Dialect; import org.hibernate.stat.QueryStatistics; import org.hibernate.test.TestCase; +import org.hibernate.test.any.PropertyValue; +import org.hibernate.test.any.StringPropertyValue; +import org.hibernate.test.any.IntegerPropertyValue; +import org.hibernate.test.any.PropertySet; +import org.hibernate.test.legacy.Bar; +import org.hibernate.test.legacy.One; import org.hibernate.test.cid.Customer; import org.hibernate.test.cid.LineItem; import org.hibernate.test.cid.Order; @@ -68,14 +74,15 @@ protected String[] getMappings() { // Make sure we are using the new AST parser translator... System.setProperty( Environment.QUERY_TRANSLATOR, "org.hibernate.hql.ast.ASTQueryTranslatorFactory" ); - return new String[]{ - "hql/Animal.hbm.xml", - "hql/FooBarCopy.hbm.xml", - "batchfetch/ProductLine.hbm.xml", - "cid/Customer.hbm.xml", - "cid/Order.hbm.xml", - "cid/LineItem.hbm.xml", - "cid/Product.hbm.xml" + return new String[] { + "hql/Animal.hbm.xml", + "hql/FooBarCopy.hbm.xml", + "batchfetch/ProductLine.hbm.xml", + "cid/Customer.hbm.xml", + "cid/Order.hbm.xml", + "cid/LineItem.hbm.xml", + "cid/Product.hbm.xml", + "any/Properties.hbm.xml" }; } @@ -86,6 +93,62 @@ } + public void testComponentQueries() { + Session s = openSession(); + s.beginTransaction(); + + // Test the ability to perform comparisions between component values + s.createQuery( "from Human h where h.name = h.name" ).list(); + s.createQuery( "from Human h where h.name = :name" ).setParameter( "name", new Name() ).list(); + s.createQuery( "from Human where name = :name" ).setParameter( "name", new Name() ).list(); + s.createQuery( "from Human h where :name = h.name" ).setParameter( "name", new Name() ).list(); + s.createQuery( "from Human h where :name <> h.name" ).setParameter( "name", new Name() ).list(); + + // Test the ability to perform comparisions between a component and an explicit row-value + s.createQuery( "from Human h where h.name = ('John', 'X', 'Doe')" ).list(); + s.createQuery( "from Human h where ('John', 'X', 'Doe') = h.name" ).list(); + s.createQuery( "from Human h where ('John', 'X', 'Doe') <> h.name" ).list(); + s.createQuery( "from Human h where ('John', 'X', 'Doe') >= h.name" ).list(); + + s.getTransaction().commit(); + s.close(); + } + + public void testAnyMappingReference() { + Session s = openSession(); + s.beginTransaction(); + + PropertyValue redValue = new StringPropertyValue( "red" ); + PropertyValue lonliestNumberValue = new IntegerPropertyValue( 1 ); + + Long id; + PropertySet ps = new PropertySet( "my properties" ); + ps.setSomeSpecificProperty( redValue ); + ps.getGeneralProperties().put( "the lonliest number", lonliestNumberValue ); + ps.getGeneralProperties().put( "i like", new StringPropertyValue( "pina coladas" ) ); + ps.getGeneralProperties().put( "i also like", new StringPropertyValue( "getting caught in the rain" ) ); + s.save( ps ); + + s.getTransaction().commit(); + id = ps.getId(); + s.clear(); + s.beginTransaction(); + + // TODO : setEntity() currently will not work here, but that would be *very* nice + // does not work because the corresponding EntityType is then used as the "bind type" rather + // than the "discovered" AnyType... + s.createQuery( "from PropertySet p where p.someSpecificProperty = :ssp" ).setParameter( "ssp", redValue ).list(); + + s.createQuery( "from PropertySet p where p.someSpecificProperty.id is not null" ).list(); + + s.createQuery( "from PropertySet p join p.generalProperties gp where gp.id is not null" ).list(); + + s.delete( s.load( PropertySet.class, id ) ); + + s.getTransaction().commit(); + s.close(); + } + public void testJdkEnumStyleEnumConstant() throws Exception { Session s = openSession(); s.beginTransaction(); Added: trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.hbm.xml 2006-06-28 02:53:39 UTC (rev 10060) @@ -0,0 +1,15 @@ +<?xml version="1.0"?> +<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > + +<hibernate-mapping package="org.hibernate.test.hql" default-access="field"> + + <class name="BooleanLiteralEntity"> + <id name="id"> + <generator class="native"/> + </id> + <property name="yesNoBoolean" column="Y_N_BOOL" type="yes_no"/> + <property name="trueFalseBoolean" column="T_F_BOOL" type="true_false"/> + <property name="zeroOneBoolean" column="NUM_BOOL" type="boolean"/> + </class> + +</hibernate-mapping> \ No newline at end of file Added: trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/BooleanLiteralEntity.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -0,0 +1,45 @@ +package org.hibernate.test.hql; + +/** + * todo: describe BooleanLiteralEntity + * + * @author Steve Ebersole + */ +public class BooleanLiteralEntity { + private Long id; + private boolean yesNoBoolean; + private boolean trueFalseBoolean; + private boolean zeroOneBoolean; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public boolean isYesNoBoolean() { + return yesNoBoolean; + } + + public void setYesNoBoolean(boolean yesNoBoolean) { + this.yesNoBoolean = yesNoBoolean; + } + + public boolean isTrueFalseBoolean() { + return trueFalseBoolean; + } + + public void setTrueFalseBoolean(boolean trueFalseBoolean) { + this.trueFalseBoolean = trueFalseBoolean; + } + + public boolean isZeroOneBoolean() { + return zeroOneBoolean; + } + + public void setZeroOneBoolean(boolean zeroOneBoolean) { + this.zeroOneBoolean = zeroOneBoolean; + } +} Modified: trunk/Hibernate3/test/org/hibernate/test/hql/BulkManipulationTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/BulkManipulationTest.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/BulkManipulationTest.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -43,7 +43,8 @@ "hql/FooBarCopy.hbm.xml", "legacy/Multi.hbm.xml", "hql/EntityWithCrazyCompositeKey.hbm.xml", - "hql/SimpleEntityWithAssociation.hbm.xml" + "hql/SimpleEntityWithAssociation.hbm.xml", + "hql/BooleanLiteralEntity.hbm.xml" }; } @@ -112,6 +113,43 @@ } + // BOOLEAN HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + public void testBooleanHandling() { + TestData data = new TestData(); + data.prepare(); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + + // currently, we need the three different binds because they are different underlying types... + int count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = :b1, trueFalseBoolean = :b2, zeroOneBoolean = :b3" ) + .setBoolean( "b1", true ) + .setBoolean( "b2", true ) + .setBoolean( "b3", true ) + .executeUpdate(); + assertEquals( 1, count ); + BooleanLiteralEntity entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult(); + assertTrue( entity.isYesNoBoolean() ); + assertTrue( entity.isTrueFalseBoolean() ); + assertTrue( entity.isZeroOneBoolean() ); + s.clear(); + + count = s.createQuery( "update BooleanLiteralEntity set yesNoBoolean = true, trueFalseBoolean = true, zeroOneBoolean = true" ) + .executeUpdate(); + assertEquals( 1, count ); + entity = ( BooleanLiteralEntity ) s.createQuery( "from BooleanLiteralEntity" ).uniqueResult(); + assertTrue( entity.isYesNoBoolean() ); + assertTrue( entity.isTrueFalseBoolean() ); + assertTrue( entity.isZeroOneBoolean() ); + + t.commit(); + s.close(); + + data.cleanup(); + } + + // INSERTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void testSimpleInsert() { @@ -411,6 +449,14 @@ // UPDATES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + public void testIncorrectSyntax() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.createQuery( "update Human set Human.description = 'xyz' where Human.id = 1 and Human.description is null" ).executeUpdate(); + t.commit(); + s.close(); + } + public void testUpdateWithWhereExistsSubquery() { // multi-table ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Session s = openSession(); @@ -1130,6 +1176,9 @@ pickup.setOwner( "Cecelia" ); s.save( pickup ); + BooleanLiteralEntity bool = new BooleanLiteralEntity(); + s.save( bool ); + txn.commit(); s.close(); } @@ -1144,6 +1193,7 @@ s.createQuery( "delete from Zoo" ).executeUpdate(); s.createQuery( "delete from Joiner" ).executeUpdate(); s.createQuery( "delete from Vehicle" ).executeUpdate(); + s.createQuery( "delete from BooleanLiteralEntity" ).executeUpdate(); txn.commit(); s.close(); Modified: trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-06-28 02:53:39 UTC (rev 10060) @@ -118,6 +118,20 @@ </subclass> </class> + <class name="One" table="one"> + <id name="key" column="one_key"> + <generator class="native" /> + </id> + <property name="x"/> + <property column="one_value" name="value"/> + </class> + <class name="Many" table="many"> + <id name="key" column="many_key"> + <generator class="native" /> + </id> + <property name="x"/> + </class> + </hibernate-mapping> Modified: trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/HQLTest.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -54,6 +54,11 @@ //FAILING TESTS: + public void testManyToAnyReferences() { + assertTranslation( "from PropertySet p where p.someSpecificProperty.id is not null" ); + assertTranslation( "from PropertySet p join p.generalProperties gp where gp.id is not null" ); + } + public void testJoinFetchCollectionOfValues() { assertTranslation( "select h from Human as h join fetch h.nickNames" ); } Modified: trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java =================================================================== --- trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java 2006-06-28 02:37:56 UTC (rev 10059) +++ trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java 2006-06-28 02:53:39 UTC (rev 10060) @@ -495,40 +495,41 @@ } protected String[] getMappings() { - return new String[]{ - "hql/Animal.hbm.xml", - "hql/EntityWithCrazyCompositeKey.hbm.xml", - "batchfetch/ProductLine.hbm.xml", - "cid/Customer.hbm.xml", - "cid/Order.hbm.xml", - "cid/LineItem.hbm.xml", - "cid/Product.hbm.xml", - "legacy/Baz.hbm.xml", - "legacy/Category.hbm.xml", - "legacy/Commento.hbm.xml", - "legacy/Container.hbm.xml", - "legacy/Custom.hbm.xml", - "legacy/Eye.hbm.xml", - "legacy/Fee.hbm.xml", - "legacy/FooBar.hbm.xml", - "legacy/Fum.hbm.xml", - "legacy/Glarch.hbm.xml", - "legacy/Holder.hbm.xml", - "legacy/Many.hbm.xml", - "legacy/Marelo.hbm.xml", - "legacy/MasterDetail.hbm.xml", - "legacy/Middle.hbm.xml", - "legacy/Multi.hbm.xml", - "legacy/Nameable.hbm.xml", - "legacy/One.hbm.xml", - "legacy/Qux.hbm.xml", - "legacy/Simple.hbm.xml", - "legacy/SingleSeveral.hbm.xml", - "legacy/WZ.hbm.xml", - "legacy/UpDown.hbm.xml", - "compositeelement/Parent.hbm.xml", - "onetoone/joined/Person.hbm.xml", - "hql/CrazyIdFieldNames.hbm.xml" + return new String[] { + "hql/Animal.hbm.xml", + "hql/EntityWithCrazyCompositeKey.hbm.xml", + "batchfetch/ProductLine.hbm.xml", + "cid/Customer.hbm.xml", + "cid/Order.hbm.xml", + "cid/LineItem.hbm.xml", + "cid/Product.hbm.xml", + "legacy/Baz.hbm.xml", + "legacy/Category.hbm.xml", + "legacy/Commento.hbm.xml", + "legacy/Container.hbm.xml", + "legacy/Custom.hbm.xml", + "legacy/Eye.hbm.xml", + "legacy/Fee.hbm.xml", + "legacy/FooBar.hbm.xml", + "legacy/Fum.hbm.xml", + "legacy/Glarch.hbm.xml", + "legacy/Holder.hbm.xml", + "legacy/Many.hbm.xml", + "legacy/Marelo.hbm.xml", + "legacy/MasterDetail.hbm.xml", + "legacy/Middle.hbm.xml", + "legacy/Multi.hbm.xml", + "legacy/Nameable.hbm.xml", + "legacy/One.hbm.xml", + "legacy/Qux.hbm.xml", + "legacy/Simple.hbm.xml", + "legacy/SingleSeveral.hbm.xml", + "legacy/WZ.hbm.xml", + "legacy/UpDown.hbm.xml", + "compositeelement/Parent.hbm.xml", + "onetoone/joined/Person.hbm.xml", + "hql/CrazyIdFieldNames.hbm.xml", + "any/Properties.hbm.xml" }; } |