HHH-15654 Fix SQL errors for some DBs + documentation for new interfaces

This commit is contained in:
Marco Belladelli 2022-12-06 10:53:35 +01:00 committed by Christian Beikov
parent 6d9c448db2
commit 5feb44026c
5 changed files with 82 additions and 21 deletions

View File

@ -12,18 +12,68 @@ import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Order;
/**
* Common contract for window parts used in window and aggregate functions.
*
* @author Marco Belladelli
*/
public interface JpaWindow {
/**
* Add partition by expressions to the window.
*
* @param expressions the partition by expressions
*
* @return the modified window
*/
JpaWindow partitionBy(Expression<?>... expressions);
/**
* Add order by expressions to the window.
*
* @param expressions the order by expressions
*
* @return the modified window
*/
JpaWindow orderBy(Order... expressions);
/**
* Add a {@code ROWS} frame clause to the window and define
* start and end {@link JpaWindowFrame} specifications.
*
* @param startFrame the start frame
* @param endFrame the optional end frame
*
* @return the modified window
*/
JpaWindow frameRows(JpaWindowFrame startFrame, JpaWindowFrame endFrame);
/**
* Add a {@code RANGE} frame clause to the window and define
* start and end {@link JpaWindowFrame} specifications.
*
* @param startFrame the start frame
* @param endFrame the optional end frame
*
* @return the modified window
*/
JpaWindow frameRange(JpaWindowFrame startFrame, JpaWindowFrame endFrame);
/**
* Add a {@code GROUPS} frame clause to the window and define
* start and end {@link JpaWindowFrame} specifications.
*
* @param startFrame the start frame
* @param endFrame the optional end frame
*
* @return the modified window
*/
JpaWindow frameGroups(JpaWindowFrame startFrame, JpaWindowFrame endFrame);
/**
* Set a {@link FrameExclusion} for this window's frame.
*
* @param frameExclusion the frame exclusion
*
* @return the modified window
*/
JpaWindow frameExclude(FrameExclusion frameExclusion);
}

View File

@ -11,10 +11,22 @@ import org.hibernate.query.sqm.FrameKind;
import jakarta.persistence.criteria.Expression;
/**
* Common contract for a {@link JpaWindow} frame specification.
*
* @author Marco Belladelli
*/
public interface JpaWindowFrame {
/**
* Get the {@link FrameKind} of this window frame.
*
* @return the window frame kind
*/
FrameKind getKind();
/**
* Get the {@link Expression} of this window frame.
*
* @return the window frame expression
*/
Expression<?> getExpression();
}

View File

@ -2643,7 +2643,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> SqmExpression<T> nthValue(Expression<T> argument, int n, JpaWindow window) {
return nthValue( argument, value( n ), window );
return nthValue( argument, literal( n ), window );
}
@Override

View File

@ -10,8 +10,11 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.dialect.PostgresPlusDialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaWindow;
@ -46,7 +49,6 @@ import static org.junit.jupiter.api.Assertions.assertNull;
@DomainModel(standardModels = StandardDomainModel.GAMBIT)
@SessionFactory
public class CriteriaOrderedSetAggregateTest {
@BeforeEach
public void prepareData(SessionFactoryScope scope) {
scope.inTransaction( em -> {
@ -104,7 +106,7 @@ public class CriteriaOrderedSetAggregateTest {
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction( session -> session.createQuery( "delete from EntityOfBasics" ).executeUpdate() );
scope.inTransaction( session -> session.createMutationQuery( "delete from EntityOfBasics" ).executeUpdate() );
}
@Test
@ -229,7 +231,7 @@ public class CriteriaOrderedSetAggregateTest {
cb.literal( 0.5 ),
root.get( "theInt" ),
SortOrder.ASCENDING,
NullPrecedence.FIRST
NullPrecedence.NONE
);
cr.select( function );
@ -242,8 +244,9 @@ public class CriteriaOrderedSetAggregateTest {
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsInverseDistributionFunctions.class)
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsWindowFunctions.class)
@SkipForDialect(dialectClass = PostgreSQLDialect.class)
@SkipForDialect(dialectClass = PostgresPlusDialect.class)
public void testInverseDistributionWithWindow(SessionFactoryScope scope) {
// note : PostgreSQL currently does not support ordered-set aggregate functions with OVER clause
// note : PostgreSQL and EDB currently does not support ordered-set aggregate functions with OVER clause
scope.inTransaction( session -> {
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Integer> cr = cb.createQuery( Integer.class );
@ -255,7 +258,7 @@ public class CriteriaOrderedSetAggregateTest {
window,
root.get( "theInt" ),
SortOrder.ASCENDING,
NullPrecedence.FIRST
NullPrecedence.NONE
);
cr.select( function ).orderBy( cb.asc( cb.literal( 1 ) ) );
@ -301,9 +304,12 @@ public class CriteriaOrderedSetAggregateTest {
} );
}
// @Test
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
@SkipForDialect(dialectClass = SQLServerDialect.class)
@SkipForDialect(dialectClass = DB2Dialect.class)
public void testHypotheticalSetRankWithGroupByHavingOrderByLimit(SessionFactoryScope scope) {
// note : this query is not translated correctly for SQLServer and DB2, skip for now
scope.inTransaction( session -> {
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Tuple> cr = cb.createQuery( Tuple.class );
@ -316,11 +322,6 @@ public class CriteriaOrderedSetAggregateTest {
.groupBy( root2.get( "id" ) ).having( cb.gt( root2.get( "id" ), cb.literal( 1 ) ) )
.orderBy( cb.asc( cb.literal( 1 ) ), cb.asc( cb.literal( 2 ) ) );
// todo marco : this test causes problems but only with mssql and db2, the sql obtained is not correct.
// we are trying to get something like this query:
// select eob2.id, rank(5) within group (order by eob.theInt asc) from EntityOfBasics eob
// cross join EntityOfBasics eob2 group by eob2.id having eob2.id > 1 order by 1,2 offset 1
List<Tuple> resultList = session.createQuery( cr ).setFirstResult( 1 ).getResultList();
assertEquals( 3, resultList.size() );
assertEquals( 1L, resultList.get( 0 ).get( 1, Long.class ) );

View File

@ -9,7 +9,6 @@ package org.hibernate.orm.test.query.criteria;
import java.util.Date;
import java.util.List;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
import org.hibernate.query.criteria.JpaExpression;
@ -104,7 +103,7 @@ public class CriteriaWindowFunctionTest {
@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.createQuery( "delete from EntityOfBasics" ).executeUpdate()
session -> session.createMutationQuery( "delete from EntityOfBasics" ).executeUpdate()
);
}
@ -156,28 +155,27 @@ public class CriteriaWindowFunctionTest {
@Test
@SkipForDialect(dialectClass = SQLServerDialect.class)
@SkipForDialect(dialectClass = DB2Dialect.class)
public void testNthValue(SessionFactoryScope scope) {
// note : SQLServer does not support nth_value function
scope.inTransaction(
session -> {
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Integer> cr = cb.createQuery( Integer.class );
Root<EntityOfBasics> root = cr.from( EntityOfBasics.class );
JpaWindow window = cb.createWindow().orderBy( cb.desc( root.get( "theInt" ) ) );
JpaWindow window = cb.createWindow()
.orderBy( cb.desc( root.get( "theInt" ) ) )
.frameRows( cb.frameUnboundedPreceding(), cb.frameUnboundedFollowing() );
JpaExpression<Integer> nthValue = cb.nthValue(
root.get( "theInt" ),
2,
window
);
// todo marco : db2 throws java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
// on getResultList() line, and SqlServer doesn't support nth_value function (even tho it's in Dialect)
cr.select( nthValue ).orderBy( cb.asc( cb.literal( 1 ), true ) );
cr.select( nthValue );
List<Integer> resultList = session.createQuery( cr ).getResultList();
assertEquals( 5, resultList.size() );
assertNull( resultList.get( 0 ) );
assertEquals( 7, resultList.get( 0 ) );
assertEquals( 7, resultList.get( 1 ) );
assertEquals( 7, resultList.get( 2 ) );
assertEquals( 7, resultList.get( 3 ) );