HHH-16465 Fix String based CTE cycle emulation
This commit is contained in:
parent
e7fa3cebb6
commit
4cb6823a05
|
@ -2489,8 +2489,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
final AbstractSqmSelfRenderingFunctionDescriptor concat = findSelfRenderingFunction( "concat", 2 );
|
final AbstractSqmSelfRenderingFunctionDescriptor concat = findSelfRenderingFunction( "concat", 2 );
|
||||||
final AbstractSqmSelfRenderingFunctionDescriptor coalesce = findSelfRenderingFunction( "coalesce", 2 );
|
final AbstractSqmSelfRenderingFunctionDescriptor coalesce = findSelfRenderingFunction( "coalesce", 2 );
|
||||||
final BasicType<String> stringType = getStringType();
|
final BasicType<String> stringType = getStringType();
|
||||||
// Shift by 1 bit instead of multiplying by 2
|
// Shift by 2 bit instead of multiplying by 4
|
||||||
final List<SqlAstNode> arguments = new ArrayList<>( currentCteStatement.getCycleColumns().size() << 1 );
|
final List<SqlAstNode> arguments = new ArrayList<>( currentCteStatement.getCycleColumns().size() << 2 );
|
||||||
final Expression nullSeparator = createNullSeparator();
|
final Expression nullSeparator = createNullSeparator();
|
||||||
|
|
||||||
if ( isInRecursiveQueryPart() ) {
|
if ( isInRecursiveQueryPart() ) {
|
||||||
|
@ -2512,6 +2512,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
.indexOf( cycleColumn );
|
.indexOf( cycleColumn );
|
||||||
final Expression selectionExpression = selectClause.getSqlSelections().get( selectionIndex )
|
final Expression selectionExpression = selectClause.getSqlSelections().get( selectionIndex )
|
||||||
.getExpression();
|
.getExpression();
|
||||||
|
arguments.add( nullSeparator );
|
||||||
arguments.add(
|
arguments.add(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"coalesce",
|
"coalesce",
|
||||||
|
@ -2564,6 +2565,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
.indexOf( cycleColumn );
|
.indexOf( cycleColumn );
|
||||||
final Expression selectionExpression = selectClause.getSqlSelections().get( selectionIndex )
|
final Expression selectionExpression = selectClause.getSqlSelections().get( selectionIndex )
|
||||||
.getExpression();
|
.getExpression();
|
||||||
|
arguments.add( nullSeparator );
|
||||||
arguments.add(
|
arguments.add(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"coalesce",
|
"coalesce",
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.query.spi.QueryImplementor;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
|
import org.hibernate.testing.orm.domain.contacts.Address;
|
||||||
|
import org.hibernate.testing.orm.domain.contacts.Contact;
|
||||||
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.JiraKey;
|
||||||
|
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
@DomainModel(standardModels = StandardDomainModel.CONTACTS)
|
||||||
|
@SessionFactory
|
||||||
|
public class CteCycleTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@JiraKey( "HHH-16465" )
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsRecursiveCtes.class)
|
||||||
|
public void testRecursiveCycleClause(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
final QueryImplementor<Tuple> query = session.createQuery(
|
||||||
|
"with alternativeContacts as (" +
|
||||||
|
"select c.alternativeContact alt from Contact c where c.id = :param " +
|
||||||
|
"union all " +
|
||||||
|
"select c.alt.alternativeContact alt from alternativeContacts c" +
|
||||||
|
")" +
|
||||||
|
"cycle alt set isCycle to true default false " +
|
||||||
|
"select ac, c.isCycle from alternativeContacts c join c.alt ac order by ac.id, c.isCycle",
|
||||||
|
Tuple.class
|
||||||
|
);
|
||||||
|
List<Tuple> list = query.setParameter( "param", 1 ).getResultList();
|
||||||
|
assertEquals( 4, list.size() );
|
||||||
|
assertEquals( "John", list.get( 0 ).get( 0, Contact.class ).getName().getFirst() );
|
||||||
|
assertEquals( "Jane", list.get( 1 ).get( 0, Contact.class ).getName().getFirst() );
|
||||||
|
assertFalse( list.get( 1 ).get( 1, Boolean.class ) );
|
||||||
|
assertEquals( "Jane", list.get( 2 ).get( 0, Contact.class ).getName().getFirst() );
|
||||||
|
assertTrue( list.get( 2 ).get( 1, Boolean.class ) );
|
||||||
|
assertEquals( "Granny", list.get( 3 ).get( 0, Contact.class ).getName().getFirst() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void prepareTestData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
final Contact contact = new Contact(
|
||||||
|
1,
|
||||||
|
new Contact.Name( "John", "Doe" ),
|
||||||
|
Contact.Gender.MALE,
|
||||||
|
LocalDate.of( 1970, 1, 1 )
|
||||||
|
);
|
||||||
|
final Contact alternativeContact = new Contact(
|
||||||
|
11,
|
||||||
|
new Contact.Name( "Jane", "Doe" ),
|
||||||
|
Contact.Gender.FEMALE,
|
||||||
|
LocalDate.of( 1970, 1, 1 )
|
||||||
|
);
|
||||||
|
final Contact alternativeContact2 = new Contact(
|
||||||
|
111,
|
||||||
|
new Contact.Name( "Granny", "Doe" ),
|
||||||
|
Contact.Gender.FEMALE,
|
||||||
|
LocalDate.of( 1970, 1, 1 )
|
||||||
|
);
|
||||||
|
alternativeContact.setAlternativeContact( alternativeContact2 );
|
||||||
|
contact.setAlternativeContact( alternativeContact );
|
||||||
|
contact.setAddresses(
|
||||||
|
List.of(
|
||||||
|
new Address( "Street 1", 1234 ),
|
||||||
|
new Address( "Street 2", 5678 )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
session.persist( alternativeContact2 );
|
||||||
|
session.persist( alternativeContact );
|
||||||
|
session.persist( contact );
|
||||||
|
alternativeContact2.setAlternativeContact( contact );
|
||||||
|
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void dropTestData(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
session.createMutationQuery( "update Contact set alternativeContact = null" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete Contact" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue