HHH-2826 : (HQL) <component> is [not] null
git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@14096 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
2af0d5b70b
commit
0a6a160709
|
@ -40,6 +40,8 @@ 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 org.hibernate.hql.ast.tree.IsNullLogicOperatorNode;
|
||||
import org.hibernate.hql.ast.tree.IsNotNullLogicOperatorNode;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
|
@ -157,7 +159,9 @@ public class SqlASTFactory extends ASTFactory implements HqlSqlTokenTypes {
|
|||
case NOT_BETWEEN:
|
||||
return BetweenOperatorNode.class;
|
||||
case IS_NULL:
|
||||
return IsNullLogicOperatorNode.class;
|
||||
case IS_NOT_NULL:
|
||||
return IsNotNullLogicOperatorNode.class;
|
||||
case EXISTS:
|
||||
return UnaryLogicOperatorNode.class;
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
|
||||
*
|
||||
* 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, v. 2.1. This program is distributed in the
|
||||
* hope that it will be useful, but WITHOUT A 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, v.2.1 along with this
|
||||
* distribution; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Red Hat Author(s): Steve Ebersole
|
||||
*/
|
||||
package org.hibernate.hql.ast.tree;
|
||||
|
||||
import antlr.collections.AST;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
|
||||
import org.hibernate.util.StringHelper;
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
/**
|
||||
* Base class for nodes dealing 'is null' and 'is not null' operators.
|
||||
* <p/>
|
||||
* todo : a good deal of this is copied from BinaryLogicOperatorNode; look at consolidating these code fragments
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractNullnessCheckNode extends UnaryLogicOperatorNode {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void initialize() {
|
||||
// TODO : this really needs to be delayed unitl after we definitively know the operand node type;
|
||||
// where this is currently a problem is parameters for which where we cannot unequivocally
|
||||
// resolve an expected type
|
||||
Type operandType = extractDataType( getOperand() );
|
||||
if ( operandType == null ) {
|
||||
return;
|
||||
}
|
||||
SessionFactoryImplementor sessionFactory = getSessionFactoryHelper().getFactory();
|
||||
int operandColumnSpan = operandType.getColumnSpan( sessionFactory );
|
||||
if ( operandColumnSpan > 1 ) {
|
||||
mutateRowValueConstructorSyntax( operandColumnSpan );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When (if) we need to expand a row value constructor, what is the type of connector to use between the
|
||||
* expansion fragments.
|
||||
*
|
||||
* @return The expansion connector type.
|
||||
*/
|
||||
protected abstract int getExpansionConnectorType();
|
||||
|
||||
/**
|
||||
* When (if) we need to expand a row value constructor, what is the text of the connector to use between the
|
||||
* expansion fragments.
|
||||
*
|
||||
* @return The expansion connector text.
|
||||
*/
|
||||
protected abstract String getExpansionConnectorText();
|
||||
|
||||
private void mutateRowValueConstructorSyntax(int operandColumnSpan) {
|
||||
final int comparisonType = getType();
|
||||
final String comparisonText = getText();
|
||||
|
||||
final int expansionConnectorType = getExpansionConnectorType();
|
||||
final String expansionConnectorText = getExpansionConnectorText();
|
||||
|
||||
setType( expansionConnectorType );
|
||||
setText( expansionConnectorText );
|
||||
|
||||
String[] mutationTexts = extractMutationTexts( getOperand(), operandColumnSpan );
|
||||
|
||||
AST container = this;
|
||||
for ( int i = operandColumnSpan - 1; i > 0; i-- ) {
|
||||
if ( i == 1 ) {
|
||||
AST op1 = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST operand1 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, mutationTexts[0] );
|
||||
op1.setFirstChild( operand1 );
|
||||
container.setFirstChild( op1 );
|
||||
AST op2 = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST operand2 = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, mutationTexts[1] );
|
||||
op2.setFirstChild( operand2 );
|
||||
op1.setNextSibling( op2 );
|
||||
}
|
||||
else {
|
||||
AST op = getASTFactory().create( comparisonType, comparisonText );
|
||||
AST operand = getASTFactory().create( HqlSqlTokenTypes.SQL_TOKEN, mutationTexts[i] );
|
||||
op.setFirstChild( operand );
|
||||
AST newContainer = getASTFactory().create( expansionConnectorType, expansionConnectorText );
|
||||
container.setFirstChild( newContainer );
|
||||
newContainer.setNextSibling( op );
|
||||
container = newContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static 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;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
|
||||
*
|
||||
* 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, v. 2.1. This program is distributed in the
|
||||
* hope that it will be useful, but WITHOUT A 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, v.2.1 along with this
|
||||
* distribution; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Red Hat Author(s): Steve Ebersole
|
||||
*/
|
||||
package org.hibernate.hql.ast.tree;
|
||||
|
||||
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
|
||||
|
||||
/**
|
||||
* IsNotNullLogicOperatorNode implementation
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class IsNotNullLogicOperatorNode extends AbstractNullnessCheckNode {
|
||||
protected int getExpansionConnectorType() {
|
||||
return HqlSqlTokenTypes.OR;
|
||||
}
|
||||
|
||||
protected String getExpansionConnectorText() {
|
||||
return "OR";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved.
|
||||
*
|
||||
* 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, v. 2.1. This program is distributed in the
|
||||
* hope that it will be useful, but WITHOUT A 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, v.2.1 along with this
|
||||
* distribution; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* Red Hat Author(s): Steve Ebersole
|
||||
*/
|
||||
package org.hibernate.hql.ast.tree;
|
||||
|
||||
import org.hibernate.hql.antlr.HqlSqlTokenTypes;
|
||||
|
||||
/**
|
||||
* Represents a 'is null' check.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class IsNullLogicOperatorNode extends AbstractNullnessCheckNode {
|
||||
protected int getExpansionConnectorType() {
|
||||
return HqlSqlTokenTypes.AND;
|
||||
}
|
||||
|
||||
protected String getExpansionConnectorText() {
|
||||
return "AND";
|
||||
}
|
||||
}
|
|
@ -4,9 +4,11 @@ import org.hibernate.type.Type;
|
|||
import org.hibernate.Hibernate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
|
||||
* Represents a unary operator node.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class UnaryLogicOperatorNode extends SqlNode implements UnaryOperatorNode {
|
||||
public class UnaryLogicOperatorNode extends HqlSqlWalkerNode implements UnaryOperatorNode {
|
||||
public Node getOperand() {
|
||||
return ( Node ) getFirstChild();
|
||||
}
|
||||
|
|
|
@ -102,6 +102,41 @@ public class ASTParserLoadingTest extends FunctionalTestCase {
|
|||
return new FunctionalTestClassTestSuite( ASTParserLoadingTest.class );
|
||||
}
|
||||
|
||||
public void testComponentNullnessChecks() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
Human h = new Human();
|
||||
h.setName( new Name( "Johnny", 'B', "Goode" ) );
|
||||
s.save( h );
|
||||
h = new Human();
|
||||
h.setName( new Name( "Steve", null, "Ebersole" ) );
|
||||
s.save( h );
|
||||
h = new Human();
|
||||
h.setName( new Name( "Bono", null, null ) );
|
||||
s.save( h );
|
||||
h = new Human();
|
||||
h.setName( new Name( null, null, null ) );
|
||||
s.save( h );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
List results = s.createQuery( "from Human where name is null" ).list();
|
||||
assertEquals( 1, results.size() );
|
||||
results = s.createQuery( "from Human where name is not null" ).list();
|
||||
assertEquals( 3, results.size() );
|
||||
s.createQuery( "from Human where ? is null" ).setParameter( 0, null ).list();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.createQuery( "delete Human" ).executeUpdate();
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
public void testInvalidCollectionDereferencesFail() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
|
|
Loading…
Reference in New Issue