diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlASTFactory.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlASTFactory.java index 293f50ffac..76d6db2ef7 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlASTFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/SqlASTFactory.java @@ -32,8 +32,8 @@ import org.hibernate.hql.internal.ast.tree.BetweenOperatorNode; import org.hibernate.hql.internal.ast.tree.BinaryArithmeticOperatorNode; import org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode; import org.hibernate.hql.internal.ast.tree.BooleanLiteralNode; -import org.hibernate.hql.internal.ast.tree.Case2Node; -import org.hibernate.hql.internal.ast.tree.CaseNode; +import org.hibernate.hql.internal.ast.tree.SearchedCaseNode; +import org.hibernate.hql.internal.ast.tree.SimpleCaseNode; import org.hibernate.hql.internal.ast.tree.CollectionFunction; import org.hibernate.hql.internal.ast.tree.ConstructorNode; import org.hibernate.hql.internal.ast.tree.CountNode; @@ -171,9 +171,9 @@ public class SqlASTFactory extends ASTFactory implements HqlSqlTokenTypes { case UNARY_PLUS: return UnaryArithmeticNode.class; case CASE2: - return Case2Node.class; + return SimpleCaseNode.class; case CASE: - return CaseNode.class; + return SearchedCaseNode.class; case PARAM: case NAMED_PARAM: return ParameterNode.class; diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/Case2Node.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/Case2Node.java deleted file mode 100644 index 2dc65faca6..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/Case2Node.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.hql.internal.ast.tree; - -import org.hibernate.hql.internal.ast.util.ColumnHelper; -import org.hibernate.type.Type; - -import antlr.SemanticException; - -/** - * Represents a case ... when .. then ... else ... end expression in a select. - * - * @author Gavin King - */ -public class Case2Node extends AbstractSelectExpression implements SelectExpression { - - public Type getDataType() { - return getFirstThenNode().getDataType(); - } - - private SelectExpression getFirstThenNode() { - return (SelectExpression) getFirstChild().getNextSibling().getFirstChild().getNextSibling(); - } - - public void setScalarColumnText(int i) throws SemanticException { - ColumnHelper.generateSingleScalarColumn( this, i ); - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/CaseNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/CaseNode.java deleted file mode 100644 index f8b73dd3bb..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/CaseNode.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as - * indicated by the @author tags or express copyright attribution - * statements applied by the authors. All third-party contributions are - * distributed under license by Red Hat Middleware LLC. - * - * This copyrighted material is made available to anyone wishing to use, modify, - * copy, or redistribute it subject to the terms and conditions of the GNU - * Lesser General Public License, as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License - * for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this distribution; if not, write to: - * Free Software Foundation, Inc. - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301 USA - * - */ -package org.hibernate.hql.internal.ast.tree; - -import org.hibernate.hql.internal.ast.util.ColumnHelper; -import org.hibernate.type.Type; - -import antlr.SemanticException; - -/** - * Represents a case ... when .. then ... else ... end expression in a select. - * - * @author Gavin King - */ -public class CaseNode extends AbstractSelectExpression implements SelectExpression { - - public Type getDataType() { - return getFirstThenNode().getDataType(); - } - - private SelectExpression getFirstThenNode() { - return (SelectExpression) getFirstChild().getFirstChild().getNextSibling(); - } - - public void setScalarColumnText(int i) throws SemanticException { - ColumnHelper.generateSingleScalarColumn( this, i ); - } - -} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MethodNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MethodNode.java index ae73c87977..e2d6df5b3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MethodNode.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/MethodNode.java @@ -133,13 +133,9 @@ public class MethodNode extends AbstractSelectExpression implements FunctionNode if ( function != null ) { AST firstChild = exprList != null ? exprList.getFirstChild() : null; Type functionReturnType = getSessionFactoryHelper() - .findFunctionReturnType( methodName, firstChild ); + .findFunctionReturnType( methodName, function, firstChild ); setDataType( functionReturnType ); } - //TODO: - /*else { - methodName = (String) getWalker().getTokenReplacements().get( methodName ); - }*/ } public boolean isCollectionPropertyMethod() { diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SearchedCaseNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SearchedCaseNode.java new file mode 100644 index 0000000000..0262907dc9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SearchedCaseNode.java @@ -0,0 +1,88 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package org.hibernate.hql.internal.ast.tree; + +import org.hibernate.QueryException; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.util.ASTUtil; +import org.hibernate.hql.internal.ast.util.ColumnHelper; +import org.hibernate.type.Type; + +import antlr.SemanticException; +import antlr.collections.AST; + +/** + * Models what ANSI SQL terms a searched case expression. This is a CASE expression + * in the form
+ * CASE + * WHEN [firstCondition] THEN [firstResult] + * WHEN [secondCondition] THEN [secondResult] + * ELSE [defaultResult] + * END + *+ * + * @author Gavin King + * @author Steve Ebersole + */ +public class SearchedCaseNode extends AbstractSelectExpression implements SelectExpression { + @Override + public Type getDataType() { + // option is used to hold each WHEN/ELSE in turn + AST option = getFirstChild(); + while ( option != null ) { + final AST result; + if ( option.getType() == HqlSqlTokenTypes.WHEN ) { + result = option.getFirstChild().getNextSibling(); + } + else if ( option.getType() == HqlSqlTokenTypes.ELSE ) { + result = option.getFirstChild(); + } + else { + throw new QueryException( + "Unexpected node type :" + + ASTUtil.getTokenTypeName( HqlSqlTokenTypes.class, option.getType() ) + + "; expecting WHEN or ELSE" + ); + } + + if ( SqlNode.class.isInstance( result ) ) { + final Type nodeDataType = ( (SqlNode) result ).getDataType(); + if ( nodeDataType != null ) { + return nodeDataType; + } + } + + option = option.getNextSibling(); + } + + throw new QueryException( "Could not determine data type for searched case statement" ); + } + + @Override + public void setScalarColumnText(int i) throws SemanticException { + ColumnHelper.generateSingleScalarColumn( this, i ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SimpleCaseNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SimpleCaseNode.java new file mode 100644 index 0000000000..e46fa7f14a --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/SimpleCaseNode.java @@ -0,0 +1,87 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package org.hibernate.hql.internal.ast.tree; + +import org.hibernate.QueryException; +import org.hibernate.hql.internal.antlr.HqlSqlTokenTypes; +import org.hibernate.hql.internal.ast.util.ASTUtil; +import org.hibernate.hql.internal.ast.util.ColumnHelper; +import org.hibernate.type.Type; + +import antlr.SemanticException; +import antlr.collections.AST; + +/** + * Models what ANSI SQL terms a simple case statement. This is a CASE expression in the form
+ * CASE [expression] + * WHEN [firstCondition] THEN [firstResult] + * WHEN [secondCondition] THEN [secondResult] + * ELSE [defaultResult] + * END + *+ * + * @author Gavin King + * @author Steve Ebersole + */ +public class SimpleCaseNode extends AbstractSelectExpression implements SelectExpression { + + public Type getDataType() { + final AST expression = getFirstChild(); + // option is used to hold each WHEN/ELSE in turn + AST option = expression.getNextSibling(); + while ( option != null ) { + final AST result; + if ( option.getType() == HqlSqlTokenTypes.WHEN ) { + result = option.getFirstChild().getNextSibling(); + } + else if ( option.getType() == HqlSqlTokenTypes.ELSE ) { + result = option.getFirstChild(); + } + else { + throw new QueryException( + "Unexpected node type :" + + ASTUtil.getTokenTypeName( HqlSqlTokenTypes.class, option.getType() ) + + "; expecting WHEN or ELSE" + ); + } + + if ( SqlNode.class.isInstance( result ) ) { + final Type nodeDataType = ( (SqlNode) result ).getDataType(); + if ( nodeDataType != null ) { + return nodeDataType; + } + } + + option = option.getNextSibling(); + } + + throw new QueryException( "Could not determine data type for simple case statement" ); + } + + public void setScalarColumnText(int i) throws SemanticException { + ColumnHelper.generateSingleScalarColumn( this, i ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java new file mode 100644 index 0000000000..165e04b39a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/CaseStatementTest.java @@ -0,0 +1,154 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.hql; + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.QueryException; +import org.hibernate.Session; +import org.hibernate.Transaction; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +/** + * @author Steve Ebersole + */ +public class CaseStatementTest extends BaseCoreFunctionalTestCase { + + @Entity(name = "Person") + public static class Person { + @Id + private Integer id; + private String name; + } + + @Override + protected Class>[] getAnnotatedClasses() { + return new Class[] { Person.class }; + } + + @Test + public void testSimpleCaseStatementFixture() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + s.createQuery( "select case p.name when 'Steve' then 'x' else 'y' end from Person p" ) + .list(); + + t.commit(); + s.close(); + } + + @Test + public void testSimpleCaseStatementWithParamResult() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + s.createQuery( "select case p.name when 'Steve' then :opt1 else p.name end from Person p" ) + .setString( "opt1", "x" ) + .list(); + + t.commit(); + s.close(); + } + + @Test + public void testSimpleCaseStatementWithParamAllResults() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + try { + s.createQuery( "select case p.name when 'Steve' then :opt1 else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + fail( "was expecting an exception" ); + } + catch (QueryException expected) { + // expected + } + + s.createQuery( "select case p.name when 'Steve' then cast( :opt1 as string ) else cast( :opt2 as string) end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + + t.commit(); + s.close(); + } + + @Test + public void testSearchedCaseStatementFixture() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + s.createQuery( "select case when p.name = 'Steve' then 'x' else 'y' end from Person p" ) + .list(); + + t.commit(); + s.close(); + } + + @Test + public void testSearchedCaseStatementWithParamResult() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + s.createQuery( "select case when p.name = 'Steve' then :opt1 else p.name end from Person p" ) + .setString( "opt1", "x" ) + .list(); + + t.commit(); + s.close(); + } + + @Test + public void testSearchedCaseStatementWithAllParamResults() { + Session s = openSession(); + Transaction t = s.beginTransaction(); + + try { + s.createQuery( "select case when p.name = 'Steve' then :opt1 else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + fail( "was expecting an exception" ); + } + catch (QueryException expected) { + // expected + } + + s.createQuery( "select case when p.name = 'Steve' then cast( :opt1 as string) else :opt2 end from Person p" ) + .setString( "opt1", "x" ) + .setString( "opt2", "y" ) + .list(); + + t.commit(); + s.close(); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/simplecase/BasicSimpleCaseTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/simplecase/BasicSimpleCaseTest.java index d8cc07b679..5a40eed3e9 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/simplecase/BasicSimpleCaseTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/criteria/simplecase/BasicSimpleCaseTest.java @@ -73,6 +73,28 @@ public class BasicSimpleCaseTest extends BaseEntityManagerFunctionalTestCase { } + @Test + public void testCaseInOrderBy2() { + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + + CriteriaBuilder builder = em.getCriteriaBuilder(); + + CriteriaQuery