HHH-16216 Add Sybase ASE test for ansinull=off and fix some issues

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2024-11-06 23:36:15 +01:00 committed by Christian Beikov
parent 31fad3bd45
commit d22aeb1a52
2 changed files with 317 additions and 58 deletions

View File

@ -359,28 +359,11 @@ protected void renderOffsetExpression(Expression offsetExpression) {
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
// In Sybase ASE, XMLTYPE is not "comparable", so we have to cast the two parts to varchar for this purpose
final boolean isLob = isLob( lhs.getExpressionType() );
final boolean ansiNullOn = ((SybaseASEDialect) getDialect()).isAnsiNullOn();
if ( isLob ) {
switch ( operator ) {
case EQUAL:
lhs.accept( this );
appendSql( " like " );
rhs.accept( this );
return;
case NOT_EQUAL:
lhs.accept( this );
appendSql( " not like " );
rhs.accept( this );
return;
default:
// Fall through
break;
}
}
// I think intersect is only supported in 16.0 SP3
if ( ( (SybaseASEDialect) getDialect() ).isAnsiNullOn() ) {
if ( isLob ) {
switch ( operator ) {
case DISTINCT_FROM:
case DISTINCT_FROM:
if ( ansiNullOn ) {
appendSql( "case when " );
lhs.accept( this );
appendSql( " like " );
@ -390,8 +373,20 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
appendSql( " is null and " );
rhs.accept( this );
appendSql( " is null then 0 else 1 end=1" );
return;
case NOT_DISTINCT_FROM:
}
else {
lhs.accept( this );
appendSql( " not like " );
rhs.accept( this );
appendSql( " and (" );
lhs.accept( this );
appendSql( " is not null or " );
rhs.accept( this );
appendSql( " is not null)" );
}
return;
case NOT_DISTINCT_FROM:
if ( ansiNullOn ) {
appendSql( "case when " );
lhs.accept( this );
appendSql( " like " );
@ -401,12 +396,42 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
appendSql( " is null and " );
rhs.accept( this );
appendSql( " is null then 0 else 1 end=0" );
return;
default:
// Fall through
break;
}
}
else {
lhs.accept( this );
appendSql( " like " );
rhs.accept( this );
appendSql( " or " );
lhs.accept( this );
appendSql( " is null and " );
rhs.accept( this );
appendSql( " is null" );
}
return;
case EQUAL:
lhs.accept( this );
appendSql( " like " );
rhs.accept( this );
return;
case NOT_EQUAL:
lhs.accept( this );
appendSql( " not like " );
rhs.accept( this );
if ( !ansiNullOn ) {
appendSql( " and " );
lhs.accept( this );
appendSql( " is not null and " );
rhs.accept( this );
appendSql( " is not null" );
}
return;
default:
// Fall through
break;
}
}
// I think intersect is only supported in 16.0 SP3
if ( ansiNullOn ) {
if ( supportsDistinctFromPredicate() ) {
renderComparisonEmulateIntersect( lhs, operator, rhs );
}
@ -417,50 +442,28 @@ protected void renderComparison(Expression lhs, ComparisonOperator operator, Exp
else {
// The ansinull setting only matters if using a parameter or literal and the eq operator according to the docs
// http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc32300.1570/html/sqlug/sqlug89.htm
boolean rhsNotNullPredicate =
lhs instanceof Literal
|| isParameter( lhs );
boolean lhsNotNullPredicate =
rhs instanceof Literal
|| isParameter( rhs );
if ( rhsNotNullPredicate || lhsNotNullPredicate ) {
boolean lhsAffectedByAnsiNullOff = lhs instanceof Literal || isParameter( lhs );
boolean rhsAffectedByAnsiNullOff = rhs instanceof Literal || isParameter( rhs );
if ( lhsAffectedByAnsiNullOff || rhsAffectedByAnsiNullOff ) {
lhs.accept( this );
switch ( operator ) {
case DISTINCT_FROM:
if ( isLob ) {
appendSql( " not like " );
}
else {
appendSql( "<>" );
}
// Since this is the ansinull=off case, this comparison is enough
appendSql( "<>" );
break;
case NOT_DISTINCT_FROM:
if ( isLob ) {
appendSql( " like " );
}
else {
appendSql( '=' );
}
// Since this is the ansinull=off case, this comparison is enough
appendSql( '=' );
break;
case LESS_THAN:
case GREATER_THAN:
case LESS_THAN_OR_EQUAL:
case GREATER_THAN_OR_EQUAL:
// These operators are not affected by ansinull=off
lhsNotNullPredicate = false;
rhsNotNullPredicate = false;
default:
appendSql( operator.sqlText() );
break;
}
rhs.accept( this );
if ( lhsNotNullPredicate ) {
if ( operator == ComparisonOperator.EQUAL || operator == ComparisonOperator.NOT_EQUAL ) {
appendSql( " and " );
lhs.accept( this );
appendSql( " is not null" );
}
if ( rhsNotNullPredicate ) {
appendSql( " and " );
appendSql( " is not null and " );
rhs.accept( this );
appendSql( " is not null" );
}

View File

@ -0,0 +1,256 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.dialect;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.dialect.SybaseASEDialect;
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.orm.test.length.WithLongStrings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
/**
* @author Jan Schatteman
*/
@Jira( value = "https://hibernate.atlassian.net/browse/HHH-16216" )
public class AnsiNullTest {
private int lob1;
private int lob2;
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.persist( new Book( 1L, "LoremIpsum" ) );
session.persist( new Book( 2L, null ) );
}
);
lob1 = scope.fromTransaction( session -> {
WithLongStrings wls = new WithLongStrings();
wls.longish = "Short String";
wls.long32 = "Some long String".repeat( 100 );
session.persist( wls );
return wls.id;
}
);
lob2 = scope.fromTransaction( session -> {
WithLongStrings wls = new WithLongStrings();
wls.longish = "Short String";
session.persist( wls );
return wls.id;
}
);
}
@AfterEach
void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createMutationQuery( "delete from Book" ).executeUpdate();
session.createMutationQuery( "delete from WithLongStrings" ).executeUpdate();
}
);
}
@Test
@RequiresDialect(value = SybaseASEDialect.class)
@DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class })
@SessionFactory
@ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull on")} )
public void testWithAnsiNullOn(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Book b = session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null", Book.class ).getSingleResult();
Assertions.assertEquals( 1L, b.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null OR b.id > 1", Book.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null OR b.id >= 1", Book.class ).list().size() );
b = session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null OR b.id < 2", Book.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null OR b.id <= 2", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title = null", Book.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title = null AND b.id <= 2", Book.class ).list().size());
b = session.createQuery( "SELECT b FROM Book b WHERE b.title = null OR b.id > 1", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null", Book.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null AND b.id < 2", Book.class ).list().size());
b = session.createQuery( "SELECT b FROM Book b WHERE b.title != null OR b.id < 2", Book.class ).getSingleResult();
Assertions.assertEquals( 1L, b.id);
b = session.createQuery( "SELECT b FROM Book b WHERE b.title != null or id = 2", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
b = session.createQuery( "SELECT b FROM Book b WHERE id = 2 or b.title != null", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE id = 1 and b.title != null", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null and id = 1", Book.class ).list().size() );
List<Book> books = session.createQuery( "SELECT b FROM Book b WHERE 1 = 1", Book.class ).list();
Assertions.assertEquals( 2, books.size());
}
);
}
@Test
@RequiresDialect(value = SybaseASEDialect.class)
@DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class })
@SessionFactory
@ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull on")} )
public void testLOBWithAnsiNullOn(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
WithLongStrings w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob1, w.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null OR w.id > 1", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null OR w.id >= 1", WithLongStrings.class ).list().size() );
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null OR w.id < 2", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null OR w.id <= 2", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null", WithLongStrings.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null AND w.id <= " + lob2, WithLongStrings.class ).list().size());
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null OR w.id > 1", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null", WithLongStrings.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null AND w.id < " + lob2, WithLongStrings.class ).list().size());
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null OR w.id > 1", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null or id = " + lob2, WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( 2L, w.id);
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE id = " + lob2 + " or w.long32 != null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( 2L, w.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE id = " + lob1 + " and w.long32 != null", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null and id = " + lob1, WithLongStrings.class ).list().size() );
List<WithLongStrings> ws = session.createQuery( "SELECT w FROM WithLongStrings w WHERE 1 = 1", WithLongStrings.class ).list();
Assertions.assertEquals( 2, ws.size());
}
);
}
@Test
@RequiresDialect(value = SybaseASEDialect.class)
@DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class })
@SessionFactory
@ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull off")} )
public void testWithAnsiNullOff(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Book b = session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null", Book.class ).getSingleResult();
Assertions.assertEquals( 1L, b.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null OR b.id > 1", Book.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS DISTINCT FROM null OR b.id >= 1", Book.class ).list().size() );
b = session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null OR b.id < 2", Book.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT b FROM Book b WHERE b.title IS NOT DISTINCT FROM null OR b.id <= 2", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title = null", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title = null AND b.id <= 2", Book.class ).list().size());
b = session.createQuery( "SELECT b FROM Book b WHERE b.title = null OR b.id > 1", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null AND b.id < 2", Book.class ).list().size());
b = session.createQuery( "SELECT b FROM Book b WHERE b.title != null OR b.id < 2", Book.class ).getSingleResult();
Assertions.assertEquals( 1L, b.id);
b = session.createQuery( "SELECT b FROM Book b WHERE b.title != null or id = 2", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
b = session.createQuery( "SELECT b FROM Book b WHERE id = 2 or b.title != null", Book.class ).getSingleResult();
Assertions.assertEquals( 2L, b.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE id = 1 and b.title != null", Book.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT b FROM Book b WHERE b.title != null and id = 1", Book.class ).list().size() );
List<Book> books = session.createQuery( "SELECT b FROM Book b WHERE 1 = 1", Book.class ).list();
Assertions.assertEquals( 2, books.size());
}
);
}
@Test
@RequiresDialect(value = SybaseASEDialect.class)
@DomainModel(annotatedClasses = { AnsiNullTest.Book.class, WithLongStrings.class })
@SessionFactory
@ServiceRegistry( settings = {@Setting(name = DriverManagerConnectionProviderImpl.INIT_SQL, value = "set ansinull off")} )
public void testLOBWithAnsiNullOff(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
WithLongStrings w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob1, w.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null OR w.id > 1", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS DISTINCT FROM null OR w.id >= 1", WithLongStrings.class ).list().size() );
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null OR w.id < 2", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 2, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 IS NOT DISTINCT FROM null OR w.id <= 2", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null", WithLongStrings.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null AND w.id <= " + lob2, WithLongStrings.class ).list().size());
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 = null OR w.id > 1", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null", WithLongStrings.class ).list().size());
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null AND w.id < " + lob2, WithLongStrings.class ).list().size());
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null OR w.id > 1", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( lob2, w.id);
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null or id = " + lob2, WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( 2L, w.id);
w = session.createQuery( "SELECT w FROM WithLongStrings w WHERE id = " + lob2 + " or w.long32 != null", WithLongStrings.class ).getSingleResult();
Assertions.assertEquals( 2L, w.id);
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE id = " + lob1 + " and w.long32 != null", WithLongStrings.class ).list().size() );
Assertions.assertEquals( 0, session.createQuery( "SELECT w FROM WithLongStrings w WHERE w.long32 != null and id = " + lob1, WithLongStrings.class ).list().size() );
List<WithLongStrings> ws = session.createQuery( "SELECT w FROM WithLongStrings w WHERE 1 = 1", WithLongStrings.class ).list();
Assertions.assertEquals( 2, ws.size());
}
);
}
@Entity(name = "Book")
static class Book {
@Id
Long id;
String title;
public Book() {
}
public Book(Long id, String title) {
this.id = id;
this.title = title;
}
}
}