diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java
index 152eedfb80..f83615af1b 100644
--- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java
+++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/BinaryLogicOperatorNode.java
@@ -23,6 +23,8 @@
*/
package org.hibernate.hql.internal.ast.tree;
+import java.util.Arrays;
+
import antlr.SemanticException;
import antlr.collections.AST;
@@ -191,9 +193,7 @@ public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryO
protected 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] = "?";
- }
+ Arrays.fill( rtn, "?" );
return rtn;
}
else if ( operand.getType() == HqlSqlTokenTypes.VECTOR_EXPR ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/InLogicOperatorNode.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/InLogicOperatorNode.java
index 7be415278f..6bb5e12421 100644
--- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/InLogicOperatorNode.java
+++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/tree/InLogicOperatorNode.java
@@ -121,50 +121,70 @@ public class InLogicOperatorNode extends BinaryLogicOperatorNode implements Bina
|| ( !ParameterNode.class.isInstance( getLeftHandOperand() ) ) ? null
: ( (ParameterNode) getLeftHandOperand() )
.getHqlParameterSpecification();
- /**
- * only one element in "in" cluster, e.g.
- * where (a,b) in ( (1,2) )
this will be mutated to
- * where a=1 and b=2
- */
+
+ final boolean negated = getType() == HqlSqlTokenTypes.NOT_IN;
+
if ( rhsNode != null && rhsNode.getNextSibling() == null ) {
- String[] rhsElementTexts = extractMutationTexts( rhsNode,
- rhsColumnSpan );
- setType( HqlSqlTokenTypes.AND );
- setText( "AND" );
- ParameterSpecification rhsEmbeddedCompositeParameterSpecification = rhsNode == null
- || ( !ParameterNode.class.isInstance( rhsNode ) ) ? null
- : ( (ParameterNode) rhsNode )
- .getHqlParameterSpecification();
- translate( lhsColumnSpan, HqlSqlTokenTypes.EQ, "=", lhsElementTexts,
+ /**
+ * only one element in the vector grouping.
+ * where (a,b) in ( (1,2) )
this will be mutated to
+ * where a=1 and b=2
+ */
+ String[] rhsElementTexts = extractMutationTexts( rhsNode, rhsColumnSpan );
+ setType( negated ? HqlTokenTypes.OR : HqlSqlTokenTypes.AND );
+ setText( negated ? "or" : "and" );
+ ParameterSpecification rhsEmbeddedCompositeParameterSpecification =
+ rhsNode == null || ( !ParameterNode.class.isInstance( rhsNode ) )
+ ? null
+ : ( (ParameterNode) rhsNode ).getHqlParameterSpecification();
+ translate(
+ lhsColumnSpan,
+ negated ? HqlSqlTokenTypes.NE : HqlSqlTokenTypes.EQ,
+ negated ? "<>" : "=",
+ lhsElementTexts,
rhsElementTexts,
lhsEmbeddedCompositeParameterSpecification,
- rhsEmbeddedCompositeParameterSpecification, this );
- } else {
+ rhsEmbeddedCompositeParameterSpecification,
+ this
+ );
+ }
+ else {
List andElementsNodeList = new ArrayList();
while ( rhsNode != null ) {
- String[] rhsElementTexts = extractMutationTexts( rhsNode,
- rhsColumnSpan );
- AST and = getASTFactory().create( HqlSqlTokenTypes.AND, "AND" );
- ParameterSpecification rhsEmbeddedCompositeParameterSpecification = rhsNode == null
- || ( !ParameterNode.class.isInstance( rhsNode ) ) ? null
- : ( (ParameterNode) rhsNode )
- .getHqlParameterSpecification();
- translate( lhsColumnSpan, HqlSqlTokenTypes.EQ, "=",
- lhsElementTexts, rhsElementTexts,
+ String[] rhsElementTexts = extractMutationTexts( rhsNode, rhsColumnSpan );
+ AST group = getASTFactory().create(
+ negated ? HqlSqlTokenTypes.OR : HqlSqlTokenTypes.AND,
+ negated ? "or" : "and"
+ );
+ ParameterSpecification rhsEmbeddedCompositeParameterSpecification =
+ rhsNode == null || ( !ParameterNode.class.isInstance( rhsNode ) )
+ ? null
+ : ( (ParameterNode) rhsNode ).getHqlParameterSpecification();
+ translate(
+ lhsColumnSpan,
+ negated ? HqlSqlTokenTypes.NE : HqlSqlTokenTypes.EQ,
+ negated ? "<>" : "=",
+ lhsElementTexts,
+ rhsElementTexts,
lhsEmbeddedCompositeParameterSpecification,
- rhsEmbeddedCompositeParameterSpecification, and );
- andElementsNodeList.add( and );
+ rhsEmbeddedCompositeParameterSpecification,
+ group
+ );
+ andElementsNodeList.add( group );
rhsNode = (Node) rhsNode.getNextSibling();
}
- setType( HqlSqlTokenTypes.OR );
- setText( "OR" );
+ setType( negated ? HqlSqlTokenTypes.AND : HqlSqlTokenTypes.OR );
+ setText( negated ? "and" : "or" );
AST curNode = this;
for ( int i = andElementsNodeList.size() - 1; i > 1; i-- ) {
- AST or = getASTFactory().create( HqlSqlTokenTypes.OR, "OR" );
- curNode.setFirstChild( or );
- curNode = or;
+ AST group = getASTFactory().create(
+ negated ? HqlSqlTokenTypes.AND : HqlSqlTokenTypes.OR,
+ negated ? "and" : "or"
+ );
+ curNode.setFirstChild( group );
+ curNode = group;
AST and = (AST) andElementsNodeList.get( i );
- or.setNextSibling( and );
+ group.setNextSibling( and );
}
AST node0 = (AST) andElementsNodeList.get( 0 );
AST node1 = (AST) andElementsNodeList.get( 1 );
diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java
new file mode 100644
index 0000000000..5e962a1638
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/hql/TupleSupportTest.java
@@ -0,0 +1,130 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2012, 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.Embeddable;
+import javax.persistence.Embedded;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+import java.util.Collections;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.engine.query.spi.HQLQueryPlan;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.hibernate.testing.TestForIssue;
+import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
+import org.hibernate.testing.junit4.BaseUnitTestCase;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Steve Ebersole
+ */
+@TestForIssue( jiraKey = "HHH-7757" )
+public class TupleSupportTest extends BaseUnitTestCase {
+ @Entity( name = "TheEntity" )
+ public static class TheEntity {
+ @Id
+ private Long id;
+ @Embedded
+ private TheComposite compositeValue;
+ }
+
+ @Embeddable
+ public static class TheComposite {
+ private String thing1;
+ private String thing2;
+
+ public TheComposite() {
+ }
+
+ public TheComposite(String thing1, String thing2) {
+ this.thing1 = thing1;
+ this.thing2 = thing2;
+ }
+ }
+
+ private SessionFactory sessionFactory;
+
+ @Before
+ public void buildSessionFactory() {
+ Configuration cfg = new Configuration()
+ .addAnnotatedClass( TheEntity.class );
+ cfg.getProperties().put( AvailableSettings.DIALECT, NoTupleSupportDialect.class.getName() );
+ cfg.getProperties().put( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
+ sessionFactory = cfg.buildSessionFactory();
+ }
+
+ @After
+ public void releaseSessionFactory() {
+ sessionFactory.close();
+ }
+
+ @Test
+ public void testImplicitTupleNotEquals() {
+ final String hql = "from TheEntity e where e.compositeValue <> :p1";
+ HQLQueryPlan queryPlan = ( (SessionFactoryImplementor) sessionFactory ).getQueryPlanCache()
+ .getHQLQueryPlan( hql, false, Collections.emptyMap() );
+
+ assertEquals( 1, queryPlan.getSqlStrings().length );
+ System.out.println( " SQL : " + queryPlan.getSqlStrings()[0] );
+ assertTrue( queryPlan.getSqlStrings()[0].contains( "<>" ) );
+ }
+
+ @Test
+ public void testImplicitTupleNotInList() {
+ final String hql = "from TheEntity e where e.compositeValue not in (:p1,:p2)";
+ HQLQueryPlan queryPlan = ( (SessionFactoryImplementor) sessionFactory ).getQueryPlanCache()
+ .getHQLQueryPlan( hql, false, Collections.emptyMap() );
+
+ assertEquals( 1, queryPlan.getSqlStrings().length );
+ System.out.println( " SQL : " + queryPlan.getSqlStrings()[0] );
+ assertTrue( queryPlan.getSqlStrings()[0].contains( "<>" ) );
+ }
+
+ public static class NoTupleSupportDialect extends H2Dialect {
+ @Override
+ public boolean supportsRowValueConstructorSyntax() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsRowValueConstructorSyntaxInInList() {
+ return false;
+ }
+ }
+}