Implement query transformer to emulate for ordered set aggregate functions through their window variants
This commit is contained in:
parent
f52cf04a16
commit
236ece769b
|
@ -313,7 +313,7 @@ public abstract class AbstractHANADialect extends Dialect {
|
||||||
|
|
||||||
functionFactory.listagg_stringAgg( "varchar" );
|
functionFactory.listagg_stringAgg( "varchar" );
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
|
|
||||||
queryEngine.getSqmFunctionRegistry().register( "timestampadd",
|
queryEngine.getSqmFunctionRegistry().register( "timestampadd",
|
||||||
new IntegralTimestampaddFunction( this, queryEngine.getTypeConfiguration() ) );
|
new IntegralTimestampaddFunction( this, queryEngine.getTypeConfiguration() ) );
|
||||||
|
|
|
@ -267,7 +267,7 @@ public class DB2Dialect extends Dialect {
|
||||||
functionFactory.listagg( null );
|
functionFactory.listagg( null );
|
||||||
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class DB2iDialect extends DB2Dialect {
|
||||||
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
functionFactory.listagg( null );
|
functionFactory.listagg( null );
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
functionFactory.listagg( null );
|
functionFactory.listagg( null );
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -330,7 +330,7 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOver(Over over) {
|
public void visitOver(Over<?> over) {
|
||||||
final Expression expression = over.getExpression();
|
final Expression expression = over.getExpression();
|
||||||
if ( expression instanceof FunctionExpression && "row_number".equals( ( (FunctionExpression) expression ).getFunctionName() ) ) {
|
if ( expression instanceof FunctionExpression && "row_number".equals( ( (FunctionExpression) expression ).getFunctionName() ) ) {
|
||||||
if ( over.getPartitions().isEmpty() && over.getOrderList().isEmpty()
|
if ( over.getPartitions().isEmpty() && over.getOrderList().isEmpty()
|
||||||
|
|
|
@ -267,8 +267,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
.setParameterTypes(INTEGER)
|
.setParameterTypes(INTEGER)
|
||||||
.register();
|
.register();
|
||||||
}
|
}
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* 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.dialect.function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.query.ReturnableType;
|
||||||
|
import org.hibernate.query.spi.NavigablePath;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
|
||||||
|
import org.hibernate.sql.ast.spi.ExpressionReplacementWalker;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryTransformer;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
import org.hibernate.sql.results.internal.ResolvedSqlSelection;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since the query spec will render a hypothetical set window function instead of an aggregate,
|
||||||
|
* the following query transformer will wrap the query spec and apply aggregation in the outer query.
|
||||||
|
* We can do this because these functions can only be used in the select clause.
|
||||||
|
* A hypothetical set aggregate function like e.g. "rank" returns the rank of a passed value within
|
||||||
|
* the ordered set as defined through the WITHIN GROUP clause.
|
||||||
|
* When used as window function, the function provides the rank of the current row within the ordered set
|
||||||
|
* as defined for the window frame through the OVER clause, but does not do aggregation.
|
||||||
|
* The aggregation effect can be achieved by:
|
||||||
|
* 1. Selecting the elements by which the ordered set is sorted
|
||||||
|
* 2. In the outer query, add a comparison predicate `function_args`=`sort_expressions`
|
||||||
|
* 3. Use an arbitrary row produced by the inner query by using e.g. the "min" function
|
||||||
|
*
|
||||||
|
* The following query
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* select rank(5) within group (order by e.num)
|
||||||
|
* from (values (1), (2), (5)) e(num)
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* can be rewritten to
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* select min(t.c1) from (
|
||||||
|
* select rank() over (order by e.num), e.num
|
||||||
|
* from (values (1), (2), (5)) e(num)
|
||||||
|
* ) t(c1,c2)
|
||||||
|
* where t.c2 = 5
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class AggregateWindowEmulationQueryTransformer implements QueryTransformer {
|
||||||
|
private final Over<Object> windowFunction;
|
||||||
|
private final List<SortSpecification> withinGroup;
|
||||||
|
private final List<SqlAstNode> arguments;
|
||||||
|
|
||||||
|
public AggregateWindowEmulationQueryTransformer(
|
||||||
|
Over<Object> windowFunction,
|
||||||
|
List<SortSpecification> withinGroup,
|
||||||
|
List<SqlAstNode> arguments) {
|
||||||
|
this.windowFunction = windowFunction;
|
||||||
|
this.withinGroup = withinGroup;
|
||||||
|
this.arguments = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QuerySpec transform(
|
||||||
|
CteContainer cteContainer,
|
||||||
|
QuerySpec querySpec,
|
||||||
|
SqmToSqlAstConverter converter) {
|
||||||
|
final SessionFactoryImplementor factory = converter.getCreationContext()
|
||||||
|
.getSessionFactory();
|
||||||
|
final QuerySpec outerQuerySpec = new QuerySpec( querySpec.isRoot() );
|
||||||
|
final String identifierVariable = "hhh_";
|
||||||
|
final NavigablePath navigablePath = new NavigablePath(
|
||||||
|
identifierVariable,
|
||||||
|
identifierVariable
|
||||||
|
);
|
||||||
|
final SelectClause selectClause = outerQuerySpec.getSelectClause();
|
||||||
|
final QuerySpec subQuerySpec = querySpec.asSubQuery();
|
||||||
|
final SelectClause subSelectClause = subQuerySpec.getSelectClause();
|
||||||
|
final List<SqlSelection> subSelections = subSelectClause.getSqlSelections();
|
||||||
|
final List<String> columnNames = new ArrayList<>( subSelections.size() );
|
||||||
|
// A map to find the select item position for an expression
|
||||||
|
// which is needed to decide if we need to introduce synthetic select items
|
||||||
|
// for group by items, since these group by items are migrated to the outer query
|
||||||
|
final Map<Expression, Integer> selectionMapping = new HashMap<>( subSelections.size() );
|
||||||
|
// Create the expressions/selections for the outer query and the columnNames list
|
||||||
|
// for the QueryPartTableGroup within which the sub query spec is embedded
|
||||||
|
for ( int i = 0; i < subSelections.size(); i++ ) {
|
||||||
|
final BasicValuedMapping mapping = (BasicValuedMapping) subSelections.get( i )
|
||||||
|
.getExpressionType();
|
||||||
|
final String columnName = "col" + i;
|
||||||
|
final ColumnReference columnReference = new ColumnReference(
|
||||||
|
identifierVariable,
|
||||||
|
columnName,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
mapping.getJdbcMapping(),
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final Expression expression = subSelections.get( i ).getExpression();
|
||||||
|
final Expression finalExpression;
|
||||||
|
if ( expression == windowFunction ) {
|
||||||
|
finalExpression = new SelfRenderingAggregateFunctionSqlAstExpression(
|
||||||
|
"min",
|
||||||
|
(sqlAppender, sqlAstArguments, walker1) -> {
|
||||||
|
sqlAppender.appendSql( "min(" );
|
||||||
|
sqlAstArguments.get( 0 ).accept( walker1 );
|
||||||
|
sqlAppender.append( ')' );
|
||||||
|
},
|
||||||
|
Collections.singletonList( columnReference ),
|
||||||
|
null,
|
||||||
|
(ReturnableType<?>) mapping.getMappedType(),
|
||||||
|
expression.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
finalExpression = columnReference;
|
||||||
|
selectionMapping.put( expression, i );
|
||||||
|
}
|
||||||
|
columnNames.add( columnName );
|
||||||
|
selectClause.addSqlSelection(
|
||||||
|
new ResolvedSqlSelection(
|
||||||
|
i + 1,
|
||||||
|
i,
|
||||||
|
finalExpression,
|
||||||
|
(BasicType<Object>) mapping.getJdbcMapping()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate the group by clause to the outer query
|
||||||
|
// and push group by expressions into the partition by clause of the window function
|
||||||
|
final List<Expression> groupByExpressions = new ArrayList<>(
|
||||||
|
subQuerySpec.getGroupByClauseExpressions().size()
|
||||||
|
);
|
||||||
|
for ( Expression groupByClauseExpression : subQuerySpec.getGroupByClauseExpressions() ) {
|
||||||
|
final Expression realExpression;
|
||||||
|
final Expression outerGroupByExpression;
|
||||||
|
if ( groupByClauseExpression instanceof SqlSelectionExpression ) {
|
||||||
|
final SqlSelection selection = ( (SqlSelectionExpression) groupByClauseExpression ).getSelection();
|
||||||
|
outerGroupByExpression = new SqlSelectionExpression(
|
||||||
|
selectClause.getSqlSelections().get( selection.getValuesArrayPosition() )
|
||||||
|
);
|
||||||
|
realExpression = selection.getExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( groupByClauseExpression instanceof SqmPathInterpretation<?> ) {
|
||||||
|
realExpression = ( (SqmPathInterpretation<?>) groupByClauseExpression ).getSqlExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
realExpression = groupByClauseExpression;
|
||||||
|
}
|
||||||
|
final Integer position = selectionMapping.get( realExpression );
|
||||||
|
if ( position == null ) {
|
||||||
|
// Group by something that has no corresponding selection item,
|
||||||
|
// so we need to introduce an intermediate selection item
|
||||||
|
final int valuesPosition = selectClause.getSqlSelections().size();
|
||||||
|
final String columnName = "col" + valuesPosition;
|
||||||
|
final JdbcMapping jdbcMapping = realExpression.getExpressionType()
|
||||||
|
.getJdbcMappings()
|
||||||
|
.get( 0 );
|
||||||
|
final ColumnReference columnReference = new ColumnReference(
|
||||||
|
identifierVariable,
|
||||||
|
columnName,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
jdbcMapping,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final int subValuesPosition = subSelectClause.getSqlSelections().size();
|
||||||
|
final SqlSelection subSelection = new ResolvedSqlSelection(
|
||||||
|
subValuesPosition + 1,
|
||||||
|
subValuesPosition,
|
||||||
|
realExpression,
|
||||||
|
(BasicType<Object>) jdbcMapping
|
||||||
|
);
|
||||||
|
columnNames.add( columnName );
|
||||||
|
subSelectClause.addSqlSelection( subSelection );
|
||||||
|
outerGroupByExpression = columnReference;
|
||||||
|
selectionMapping.put( realExpression, subValuesPosition );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outerGroupByExpression = new SqlSelectionExpression(
|
||||||
|
selectClause.getSqlSelections().get( position )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
windowFunction.getPartitions().add( realExpression );
|
||||||
|
groupByExpressions.add( outerGroupByExpression );
|
||||||
|
}
|
||||||
|
outerQuerySpec.setGroupByClauseExpressions( groupByExpressions );
|
||||||
|
subQuerySpec.setGroupByClauseExpressions( null );
|
||||||
|
|
||||||
|
// Migrate the having clause to the outer query
|
||||||
|
if ( subQuerySpec.getHavingClauseRestrictions() != null ) {
|
||||||
|
final Predicate predicate = new ExpressionReplacementWalker() {
|
||||||
|
@Override
|
||||||
|
protected <X extends SqlAstNode> X replaceExpression(X expression) {
|
||||||
|
if ( expression instanceof Literal || expression instanceof JdbcParameter ) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
final Expression outerExpression;
|
||||||
|
if ( expression instanceof SqlSelectionExpression ) {
|
||||||
|
final SqlSelection selection = ( (SqlSelectionExpression) expression ).getSelection();
|
||||||
|
outerExpression = selectClause.getSqlSelections()
|
||||||
|
.get( selection.getValuesArrayPosition() )
|
||||||
|
.getExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Expression realExpression;
|
||||||
|
if ( expression instanceof SqmPathInterpretation<?> ) {
|
||||||
|
realExpression = ( (SqmPathInterpretation<?>) expression ).getSqlExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
realExpression = (Expression) expression;
|
||||||
|
}
|
||||||
|
final Integer position = selectionMapping.get( realExpression );
|
||||||
|
if ( position == null ) {
|
||||||
|
// An expression that has no corresponding selection item,
|
||||||
|
// so we need to introduce an intermediate selection item
|
||||||
|
final int valuesPosition = selectClause.getSqlSelections().size();
|
||||||
|
final String columnName = "col" + valuesPosition;
|
||||||
|
final JdbcMapping jdbcMapping = realExpression.getExpressionType()
|
||||||
|
.getJdbcMappings()
|
||||||
|
.get( 0 );
|
||||||
|
final ColumnReference columnReference = new ColumnReference(
|
||||||
|
identifierVariable,
|
||||||
|
columnName,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
jdbcMapping,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final int subValuesPosition = subSelectClause.getSqlSelections().size();
|
||||||
|
final SqlSelection subSelection = new ResolvedSqlSelection(
|
||||||
|
subValuesPosition + 1,
|
||||||
|
subValuesPosition,
|
||||||
|
realExpression,
|
||||||
|
(BasicType<Object>) jdbcMapping
|
||||||
|
);
|
||||||
|
columnNames.add( columnName );
|
||||||
|
subSelectClause.addSqlSelection( subSelection );
|
||||||
|
outerExpression = columnReference;
|
||||||
|
selectionMapping.put( realExpression, subValuesPosition );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outerExpression = selectClause.getSqlSelections().get( position )
|
||||||
|
.getExpression();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (X) outerExpression;
|
||||||
|
}
|
||||||
|
}.replaceExpressions( subQuerySpec.getHavingClauseRestrictions() );
|
||||||
|
outerQuerySpec.setHavingClauseRestrictions( predicate );
|
||||||
|
subQuerySpec.setHavingClauseRestrictions( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate the order by clause to the outer query
|
||||||
|
if ( subQuerySpec.hasSortSpecifications() ) {
|
||||||
|
for ( SortSpecification sortSpecification : subQuerySpec.getSortSpecifications() ) {
|
||||||
|
final Expression sortExpression = sortSpecification.getSortExpression();
|
||||||
|
final Expression outerSortExpression;
|
||||||
|
if ( sortExpression instanceof SqlSelectionExpression ) {
|
||||||
|
final SqlSelection selection = ( (SqlSelectionExpression) sortExpression ).getSelection();
|
||||||
|
outerSortExpression = new SqlSelectionExpression(
|
||||||
|
selectClause.getSqlSelections()
|
||||||
|
.get( selection.getValuesArrayPosition() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final Expression realExpression;
|
||||||
|
if ( sortExpression instanceof SqmPathInterpretation<?> ) {
|
||||||
|
realExpression = ( (SqmPathInterpretation<?>) sortExpression ).getSqlExpression();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
realExpression = sortExpression;
|
||||||
|
}
|
||||||
|
final Integer position = selectionMapping.get( realExpression );
|
||||||
|
if ( position == null ) {
|
||||||
|
// Group by something that has no corresponding selection item,
|
||||||
|
// so we need to introduce an intermediate selection item
|
||||||
|
final int valuesPosition = selectClause.getSqlSelections().size();
|
||||||
|
final String columnName = "col" + valuesPosition;
|
||||||
|
final JdbcMapping jdbcMapping = realExpression.getExpressionType()
|
||||||
|
.getJdbcMappings()
|
||||||
|
.get( 0 );
|
||||||
|
final ColumnReference columnReference = new ColumnReference(
|
||||||
|
identifierVariable,
|
||||||
|
columnName,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
jdbcMapping,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final int subValuesPosition = subSelectClause.getSqlSelections().size();
|
||||||
|
final SqlSelection subSelection = new ResolvedSqlSelection(
|
||||||
|
subValuesPosition + 1,
|
||||||
|
subValuesPosition,
|
||||||
|
realExpression,
|
||||||
|
(BasicType<Object>) jdbcMapping
|
||||||
|
);
|
||||||
|
columnNames.add( columnName );
|
||||||
|
subSelectClause.addSqlSelection( subSelection );
|
||||||
|
outerSortExpression = columnReference;
|
||||||
|
selectionMapping.put( realExpression, subValuesPosition );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outerSortExpression = new SqlSelectionExpression(
|
||||||
|
selectClause.getSqlSelections().get( position )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outerQuerySpec.addSortSpecification(
|
||||||
|
new SortSpecification(
|
||||||
|
outerSortExpression,
|
||||||
|
sortSpecification.getSortOrder(),
|
||||||
|
sortSpecification.getNullPrecedence()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
subQuerySpec.getSortSpecifications().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to add selection items for the expressions we order by to the sub query spec.
|
||||||
|
final int selectionOffset = columnNames.size();
|
||||||
|
// Collect the sorting column references so we can apply the filter later
|
||||||
|
final List<ColumnReference> sortingColumns = new ArrayList<>( withinGroup.size() );
|
||||||
|
for ( int i = 0; i < withinGroup.size(); i++ ) {
|
||||||
|
final int valueIndex = selectionOffset + i;
|
||||||
|
final Expression sortExpression = withinGroup.get( i ).getSortExpression();
|
||||||
|
final BasicValuedMapping mapping = (BasicValuedMapping) sortExpression.getExpressionType();
|
||||||
|
final String columnName = "col" + valueIndex;
|
||||||
|
final int oldValueIndex = subSelectClause.getSqlSelections().size();
|
||||||
|
columnNames.add( columnName );
|
||||||
|
subSelectClause.addSqlSelection(
|
||||||
|
new ResolvedSqlSelection(
|
||||||
|
oldValueIndex + 1,
|
||||||
|
oldValueIndex,
|
||||||
|
sortExpression,
|
||||||
|
(BasicType<Object>) mapping.getJdbcMapping()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
sortingColumns.add(
|
||||||
|
new ColumnReference(
|
||||||
|
identifierVariable,
|
||||||
|
columnName,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
mapping.getJdbcMapping(),
|
||||||
|
factory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( arguments != null ) {
|
||||||
|
// Hypothetical set aggregate functions usually provide some rank based on a value
|
||||||
|
// i.e. which rank does the value 5 have when ordering by a column ascending
|
||||||
|
// So we add a filter to the outer query so we can extract the rank
|
||||||
|
switch ( arguments.size() ) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
outerQuerySpec.applyPredicate(
|
||||||
|
new ComparisonPredicate(
|
||||||
|
sortingColumns.get( 0 ),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
(Expression) arguments.get( 0 )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
outerQuerySpec.applyPredicate(
|
||||||
|
new ComparisonPredicate(
|
||||||
|
new SqlTuple( sortingColumns, null ),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
new SqlTuple(
|
||||||
|
(List<? extends Expression>) (List<?>) arguments,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final QueryPartTableGroup queryPartTableGroup = new QueryPartTableGroup(
|
||||||
|
navigablePath,
|
||||||
|
null,
|
||||||
|
subQuerySpec,
|
||||||
|
identifierVariable,
|
||||||
|
columnNames,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
outerQuerySpec.getFromClause().addRoot( queryPartTableGroup );
|
||||||
|
|
||||||
|
// Migrate the offset/fetch clause
|
||||||
|
outerQuerySpec.setOffsetClauseExpression( subQuerySpec.getOffsetClauseExpression() );
|
||||||
|
outerQuerySpec.setFetchClauseExpression(
|
||||||
|
subQuerySpec.getFetchClauseExpression(),
|
||||||
|
subQuerySpec.getFetchClauseType()
|
||||||
|
);
|
||||||
|
subQuerySpec.setOffsetClauseExpression( null );
|
||||||
|
subQuerySpec.setFetchClauseExpression( null, null );
|
||||||
|
|
||||||
|
return outerQuerySpec;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,14 +43,14 @@ public class CommonFunctionFactory {
|
||||||
private final BasicType<Date> dateType;
|
private final BasicType<Date> dateType;
|
||||||
private final BasicType<Date> timeType;
|
private final BasicType<Date> timeType;
|
||||||
private final BasicType<Date> timestampType;
|
private final BasicType<Date> timestampType;
|
||||||
|
|
||||||
private final SqmFunctionRegistry functionRegistry;
|
private final SqmFunctionRegistry functionRegistry;
|
||||||
private final TypeConfiguration typeConfiguration;
|
private final TypeConfiguration typeConfiguration;
|
||||||
|
|
||||||
public CommonFunctionFactory(QueryEngine queryEngine) {
|
public CommonFunctionFactory(QueryEngine queryEngine) {
|
||||||
functionRegistry = queryEngine.getSqmFunctionRegistry();
|
functionRegistry = queryEngine.getSqmFunctionRegistry();
|
||||||
typeConfiguration = queryEngine.getTypeConfiguration();
|
typeConfiguration = queryEngine.getTypeConfiguration();
|
||||||
|
|
||||||
BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
|
||||||
dateType = basicTypeRegistry.resolve(StandardBasicTypes.DATE);
|
dateType = basicTypeRegistry.resolve(StandardBasicTypes.DATE);
|
||||||
timeType = basicTypeRegistry.resolve(StandardBasicTypes.TIME);
|
timeType = basicTypeRegistry.resolve(StandardBasicTypes.TIME);
|
||||||
|
@ -1778,6 +1778,17 @@ public class CommonFunctionFactory {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void inverseDistributionOrderedSetAggregates_windowEmulation() {
|
||||||
|
functionRegistry.register(
|
||||||
|
"percentile_cont",
|
||||||
|
new InverseDistributionWindowEmulation( "percentile_cont", NUMERIC, typeConfiguration )
|
||||||
|
);
|
||||||
|
functionRegistry.register(
|
||||||
|
"percentile_disc",
|
||||||
|
new InverseDistributionWindowEmulation( "percentile_disc", NUMERIC, typeConfiguration )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void hypotheticalOrderedSetAggregates() {
|
public void hypotheticalOrderedSetAggregates() {
|
||||||
functionRegistry.register(
|
functionRegistry.register(
|
||||||
"rank",
|
"rank",
|
||||||
|
@ -1797,6 +1808,25 @@ public class CommonFunctionFactory {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void hypotheticalOrderedSetAggregates_windowEmulation() {
|
||||||
|
functionRegistry.register(
|
||||||
|
"rank",
|
||||||
|
new HypotheticalSetWindowEmulation( "rank", StandardBasicTypes.LONG, typeConfiguration )
|
||||||
|
);
|
||||||
|
functionRegistry.register(
|
||||||
|
"dense_rank",
|
||||||
|
new HypotheticalSetWindowEmulation( "dense_rank", StandardBasicTypes.LONG, typeConfiguration )
|
||||||
|
);
|
||||||
|
functionRegistry.register(
|
||||||
|
"percent_rank",
|
||||||
|
new HypotheticalSetWindowEmulation( "percent_rank", StandardBasicTypes.DOUBLE, typeConfiguration )
|
||||||
|
);
|
||||||
|
functionRegistry.register(
|
||||||
|
"cume_dist",
|
||||||
|
new HypotheticalSetWindowEmulation( "cume_dist", StandardBasicTypes.DOUBLE, typeConfiguration )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void math() {
|
public void math() {
|
||||||
functionRegistry.namedDescriptorBuilder( "round" )
|
functionRegistry.namedDescriptorBuilder( "round" )
|
||||||
// To avoid truncating to a specific data type, we default to using the argument type
|
// To avoid truncating to a specific data type, we default to using the argument type
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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.dialect.function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.query.ReturnableType;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingOrderedSetAggregateFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmAggregateFunction;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmOrderedSetAggregateFunction;
|
||||||
|
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmOrderByClause;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
import org.hibernate.type.BasicTypeReference;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class HypotheticalSetWindowEmulation extends HypotheticalSetFunction {
|
||||||
|
|
||||||
|
public HypotheticalSetWindowEmulation(String name, BasicTypeReference<?> returnType, TypeConfiguration typeConfiguration) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
returnType,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> SelfRenderingSqmAggregateFunction<T> generateSqmOrderedSetAggregateFunctionExpression(
|
||||||
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
SqmPredicate filter,
|
||||||
|
SqmOrderByClause withinGroupClause,
|
||||||
|
ReturnableType<T> impliedResultType,
|
||||||
|
QueryEngine queryEngine,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return new SelfRenderingSqmOrderedSetAggregateFunction<>(
|
||||||
|
this,
|
||||||
|
this,
|
||||||
|
arguments,
|
||||||
|
filter,
|
||||||
|
withinGroupClause,
|
||||||
|
impliedResultType,
|
||||||
|
getArgumentsValidator(),
|
||||||
|
getReturnTypeResolver(),
|
||||||
|
queryEngine.getCriteriaBuilder(),
|
||||||
|
getName()
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression convertToSqlAst(SqmToSqlAstConverter walker) {
|
||||||
|
final Clause currentClause = walker.getCurrentClauseStack().getCurrent();
|
||||||
|
if ( currentClause == Clause.OVER ) {
|
||||||
|
return super.convertToSqlAst( walker );
|
||||||
|
}
|
||||||
|
else if ( currentClause != Clause.SELECT ) {
|
||||||
|
throw new IllegalArgumentException( "Can't emulate [" + getName() + "] in clause " + currentClause + ". Only the SELECT clause is supported!" );
|
||||||
|
}
|
||||||
|
final ReturnableType<?> resultType = resolveResultType(
|
||||||
|
walker.getCreationContext().getMappingMetamodel().getTypeConfiguration()
|
||||||
|
);
|
||||||
|
|
||||||
|
List<SqlAstNode> arguments = resolveSqlAstArguments( getArguments(), walker );
|
||||||
|
ArgumentsValidator argumentsValidator = getArgumentsValidator();
|
||||||
|
if ( argumentsValidator != null ) {
|
||||||
|
argumentsValidator.validateSqlTypes( arguments, getFunctionName() );
|
||||||
|
}
|
||||||
|
List<SortSpecification> withinGroup;
|
||||||
|
if ( this.getWithinGroup() == null ) {
|
||||||
|
withinGroup = Collections.emptyList();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
walker.getCurrentClauseStack().push( Clause.ORDER );
|
||||||
|
try {
|
||||||
|
final List<SqmSortSpecification> sortSpecifications = this.getWithinGroup().getSortSpecifications();
|
||||||
|
withinGroup = new ArrayList<>( sortSpecifications.size() );
|
||||||
|
for ( SqmSortSpecification sortSpecification : sortSpecifications ) {
|
||||||
|
final SortSpecification specification = (SortSpecification) walker.visitSortSpecification( sortSpecification );
|
||||||
|
if ( specification != null ) {
|
||||||
|
withinGroup.add( specification );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
walker.getCurrentClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final SelfRenderingFunctionSqlAstExpression function = new SelfRenderingOrderedSetAggregateFunctionSqlAstExpression(
|
||||||
|
getFunctionName(),
|
||||||
|
getRenderingSupport(),
|
||||||
|
Collections.emptyList(),
|
||||||
|
getFilter() == null ? null : (Predicate) getFilter().accept( walker ),
|
||||||
|
Collections.emptyList(),
|
||||||
|
resultType,
|
||||||
|
getMappingModelExpressible( walker, resultType )
|
||||||
|
);
|
||||||
|
final Over<Object> windowFunction = new Over<>( function, new ArrayList<>(), withinGroup );
|
||||||
|
walker.registerQueryTransformer(
|
||||||
|
new AggregateWindowEmulationQueryTransformer( windowFunction, withinGroup, arguments )
|
||||||
|
);
|
||||||
|
return windowFunction;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,62 +57,13 @@ public class InverseDistributionFunction extends AbstractSqmSelfRenderingFunctio
|
||||||
ReturnableType<T> impliedResultType,
|
ReturnableType<T> impliedResultType,
|
||||||
QueryEngine queryEngine,
|
QueryEngine queryEngine,
|
||||||
TypeConfiguration typeConfiguration) {
|
TypeConfiguration typeConfiguration) {
|
||||||
return new SelfRenderingSqmOrderedSetAggregateFunction<>(
|
return new SelfRenderingInverseDistributionFunction<>(
|
||||||
this,
|
|
||||||
this,
|
|
||||||
arguments,
|
arguments,
|
||||||
filter,
|
filter,
|
||||||
withinGroupClause,
|
withinGroupClause,
|
||||||
impliedResultType,
|
impliedResultType,
|
||||||
getArgumentsValidator(),
|
queryEngine
|
||||||
getReturnTypeResolver(),
|
);
|
||||||
queryEngine.getCriteriaBuilder(),
|
|
||||||
getName()
|
|
||||||
) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ReturnableType<?> resolveResultType(TypeConfiguration typeConfiguration) {
|
|
||||||
return (ReturnableType<?>) withinGroupClause.getSortSpecifications().get( 0 ).getSortExpression()
|
|
||||||
.getExpressible();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected MappingModelExpressible<?> getMappingModelExpressible(
|
|
||||||
SqmToSqlAstConverter walker,
|
|
||||||
ReturnableType<?> resultType) {
|
|
||||||
MappingModelExpressible<?> mapping;
|
|
||||||
if ( resultType instanceof MappingModelExpressible) {
|
|
||||||
// here we have a BasicType, which can be cast
|
|
||||||
// directly to BasicValuedMapping
|
|
||||||
mapping = (MappingModelExpressible<?>) resultType;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// here we have something that is not a BasicType,
|
|
||||||
// and we have no way to get a BasicValuedMapping
|
|
||||||
// from it directly
|
|
||||||
final Expression expression = (Expression) withinGroupClause.getSortSpecifications().get( 0 )
|
|
||||||
.getSortExpression()
|
|
||||||
.accept( walker );
|
|
||||||
if ( expression.getExpressionType() instanceof BasicValuedMapping ) {
|
|
||||||
return (BasicValuedMapping) expression.getExpressionType();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
final MappingMetamodelImplementor domainModel = walker.getCreationContext()
|
|
||||||
.getSessionFactory()
|
|
||||||
.getRuntimeMetamodels()
|
|
||||||
.getMappingMetamodel();
|
|
||||||
return domainModel.resolveMappingExpressible(
|
|
||||||
getNodeType(),
|
|
||||||
walker.getFromClauseAccess()::getTableGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
return null; // this works at least approximately
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mapping;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -174,4 +125,71 @@ public class InverseDistributionFunction extends AbstractSqmSelfRenderingFunctio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class SelfRenderingInverseDistributionFunction<T> extends SelfRenderingSqmOrderedSetAggregateFunction<T> {
|
||||||
|
|
||||||
|
private final SqmOrderByClause withinGroupClause;
|
||||||
|
|
||||||
|
public SelfRenderingInverseDistributionFunction(
|
||||||
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
SqmPredicate filter,
|
||||||
|
SqmOrderByClause withinGroupClause,
|
||||||
|
ReturnableType<T> impliedResultType, QueryEngine queryEngine) {
|
||||||
|
super(
|
||||||
|
InverseDistributionFunction.this,
|
||||||
|
InverseDistributionFunction.this,
|
||||||
|
arguments,
|
||||||
|
filter,
|
||||||
|
withinGroupClause,
|
||||||
|
impliedResultType,
|
||||||
|
InverseDistributionFunction.this.getArgumentsValidator(),
|
||||||
|
InverseDistributionFunction.this.getReturnTypeResolver(),
|
||||||
|
queryEngine.getCriteriaBuilder(),
|
||||||
|
InverseDistributionFunction.this.getName()
|
||||||
|
);
|
||||||
|
this.withinGroupClause = withinGroupClause;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ReturnableType<?> resolveResultType(TypeConfiguration typeConfiguration) {
|
||||||
|
return (ReturnableType<?>) withinGroupClause.getSortSpecifications().get( 0 ).getSortExpression()
|
||||||
|
.getExpressible();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MappingModelExpressible<?> getMappingModelExpressible(
|
||||||
|
SqmToSqlAstConverter walker,
|
||||||
|
ReturnableType<?> resultType) {
|
||||||
|
MappingModelExpressible<?> mapping;
|
||||||
|
if ( resultType instanceof MappingModelExpressible) {
|
||||||
|
// here we have a BasicType, which can be cast
|
||||||
|
// directly to BasicValuedMapping
|
||||||
|
mapping = (MappingModelExpressible<?>) resultType;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// here we have something that is not a BasicType,
|
||||||
|
// and we have no way to get a BasicValuedMapping
|
||||||
|
// from it directly
|
||||||
|
final Expression expression = (Expression) withinGroupClause.getSortSpecifications().get( 0 )
|
||||||
|
.getSortExpression()
|
||||||
|
.accept( walker );
|
||||||
|
if ( expression.getExpressionType() instanceof BasicValuedMapping ) {
|
||||||
|
return (BasicValuedMapping) expression.getExpressionType();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final MappingMetamodelImplementor domainModel = walker.getCreationContext()
|
||||||
|
.getSessionFactory()
|
||||||
|
.getRuntimeMetamodels()
|
||||||
|
.getMappingMetamodel();
|
||||||
|
return domainModel.resolveMappingExpressible(
|
||||||
|
getNodeType(),
|
||||||
|
walker.getFromClauseAccess()::getTableGroup
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
return null; // this works at least approximately
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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.dialect.function;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.query.ReturnableType;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingOrderedSetAggregateFunctionSqlAstExpression;
|
||||||
|
import org.hibernate.query.sqm.function.SelfRenderingSqmAggregateFunction;
|
||||||
|
import org.hibernate.query.sqm.produce.function.ArgumentsValidator;
|
||||||
|
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||||
|
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
|
||||||
|
import org.hibernate.query.sqm.tree.SqmTypedNode;
|
||||||
|
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmOrderByClause;
|
||||||
|
import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class InverseDistributionWindowEmulation extends InverseDistributionFunction {
|
||||||
|
|
||||||
|
public InverseDistributionWindowEmulation(String name, FunctionParameterType parameterType, TypeConfiguration typeConfiguration) {
|
||||||
|
super(
|
||||||
|
name,
|
||||||
|
parameterType,
|
||||||
|
typeConfiguration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> SelfRenderingSqmAggregateFunction<T> generateSqmOrderedSetAggregateFunctionExpression(
|
||||||
|
List<? extends SqmTypedNode<?>> arguments,
|
||||||
|
SqmPredicate filter,
|
||||||
|
SqmOrderByClause withinGroupClause,
|
||||||
|
ReturnableType<T> impliedResultType,
|
||||||
|
QueryEngine queryEngine,
|
||||||
|
TypeConfiguration typeConfiguration) {
|
||||||
|
return new SelfRenderingInverseDistributionFunction<>(
|
||||||
|
arguments,
|
||||||
|
filter,
|
||||||
|
withinGroupClause,
|
||||||
|
impliedResultType,
|
||||||
|
queryEngine
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Expression convertToSqlAst(SqmToSqlAstConverter walker) {
|
||||||
|
final Clause currentClause = walker.getCurrentClauseStack().getCurrent();
|
||||||
|
if ( currentClause == Clause.OVER ) {
|
||||||
|
return super.convertToSqlAst( walker );
|
||||||
|
}
|
||||||
|
else if ( currentClause != Clause.SELECT ) {
|
||||||
|
throw new IllegalArgumentException( "Can't emulate [" + getName() + "] in clause " + currentClause + ". Only the SELECT clause is supported!" );
|
||||||
|
}
|
||||||
|
final ReturnableType<?> resultType = resolveResultType(
|
||||||
|
walker.getCreationContext().getMappingMetamodel().getTypeConfiguration()
|
||||||
|
);
|
||||||
|
|
||||||
|
List<SqlAstNode> arguments = resolveSqlAstArguments( getArguments(), walker );
|
||||||
|
ArgumentsValidator argumentsValidator = getArgumentsValidator();
|
||||||
|
if ( argumentsValidator != null ) {
|
||||||
|
argumentsValidator.validateSqlTypes( arguments, getFunctionName() );
|
||||||
|
}
|
||||||
|
List<SortSpecification> withinGroup;
|
||||||
|
if ( this.getWithinGroup() == null ) {
|
||||||
|
withinGroup = Collections.emptyList();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
walker.getCurrentClauseStack().push( Clause.ORDER );
|
||||||
|
try {
|
||||||
|
final List<SqmSortSpecification> sortSpecifications = this.getWithinGroup().getSortSpecifications();
|
||||||
|
withinGroup = new ArrayList<>( sortSpecifications.size() );
|
||||||
|
for ( SqmSortSpecification sortSpecification : sortSpecifications ) {
|
||||||
|
final SortSpecification specification = (SortSpecification) walker.visitSortSpecification( sortSpecification );
|
||||||
|
if ( specification != null ) {
|
||||||
|
withinGroup.add( specification );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
walker.getCurrentClauseStack().pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final SelfRenderingFunctionSqlAstExpression function = new SelfRenderingOrderedSetAggregateFunctionSqlAstExpression(
|
||||||
|
getFunctionName(),
|
||||||
|
getRenderingSupport(),
|
||||||
|
arguments,
|
||||||
|
getFilter() == null ? null : (Predicate) getFilter().accept( walker ),
|
||||||
|
withinGroup,
|
||||||
|
resultType,
|
||||||
|
getMappingModelExpressible( walker, resultType )
|
||||||
|
);
|
||||||
|
final Over<Object> windowFunction = new Over<>( function, new ArrayList<>(), Collections.emptyList() );
|
||||||
|
walker.registerQueryTransformer(
|
||||||
|
new AggregateWindowEmulationQueryTransformer( windowFunction, withinGroup, null )
|
||||||
|
);
|
||||||
|
return windowFunction;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -272,7 +272,7 @@ public class CteInsertHandler implements InsertHandler {
|
||||||
new SqlSelectionImpl(
|
new SqlSelectionImpl(
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
new Over(
|
new Over<>(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"row_number",
|
"row_number",
|
||||||
(appender, args, walker) -> appender.appendSql(
|
(appender, args, walker) -> appender.appendSql(
|
||||||
|
@ -985,7 +985,7 @@ public class CteInsertHandler implements InsertHandler {
|
||||||
new SqlSelectionImpl(
|
new SqlSelectionImpl(
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
new Over(
|
new Over<>(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"row_number",
|
"row_number",
|
||||||
(appender, args, walker) -> appender.appendSql(
|
(appender, args, walker) -> appender.appendSql(
|
||||||
|
|
|
@ -222,7 +222,7 @@ public class TableBasedInsertHandler implements InsertHandler {
|
||||||
new SqlSelectionImpl(
|
new SqlSelectionImpl(
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
new Over(
|
new Over<>(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"row_number",
|
"row_number",
|
||||||
(appender, args, walker) -> appender.appendSql(
|
(appender, args, walker) -> appender.appendSql(
|
||||||
|
|
|
@ -1296,7 +1296,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
||||||
identifierSelection = new SqlSelectionImpl(
|
identifierSelection = new SqlSelectionImpl(
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
new Over(
|
new Over<>(
|
||||||
new SelfRenderingFunctionSqlAstExpression(
|
new SelfRenderingFunctionSqlAstExpression(
|
||||||
"row_number",
|
"row_number",
|
||||||
(appender, args, walker) -> appender.appendSql( "row_number()" ),
|
(appender, args, walker) -> appender.appendSql( "row_number()" ),
|
||||||
|
|
|
@ -123,7 +123,7 @@ public interface SqlAstWalker {
|
||||||
|
|
||||||
void visitDistinct(Distinct distinct);
|
void visitDistinct(Distinct distinct);
|
||||||
|
|
||||||
void visitOverflow(Overflow distinct);
|
void visitOverflow(Overflow overflow);
|
||||||
|
|
||||||
void visitStar(Star star);
|
void visitStar(Star star);
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ public interface SqlAstWalker {
|
||||||
|
|
||||||
void visitSummarization(Summarization every);
|
void visitSummarization(Summarization every);
|
||||||
|
|
||||||
void visitOver(Over over);
|
void visitOver(Over<?> over);
|
||||||
|
|
||||||
void visitSelfRenderingExpression(SelfRenderingExpression expression);
|
void visitSelfRenderingExpression(SelfRenderingExpression expression);
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.expression.LiteralAsParameter;
|
import org.hibernate.sql.ast.tree.expression.LiteralAsParameter;
|
||||||
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
|
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.OrderedSetAggregateFunctionExpression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Over;
|
import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
import org.hibernate.sql.ast.tree.expression.Overflow;
|
import org.hibernate.sql.ast.tree.expression.Overflow;
|
||||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
|
@ -3242,8 +3243,17 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOver(Over over) {
|
public void visitOver(Over<?> over) {
|
||||||
over.getExpression().accept( this );
|
final Expression overExpression = over.getExpression();
|
||||||
|
overExpression.accept( this );
|
||||||
|
final boolean orderedSetAggregate;
|
||||||
|
if ( overExpression instanceof OrderedSetAggregateFunctionExpression ) {
|
||||||
|
final OrderedSetAggregateFunctionExpression expression = (OrderedSetAggregateFunctionExpression) overExpression;
|
||||||
|
orderedSetAggregate = expression.getWithinGroup() != null && !expression.getWithinGroup().isEmpty();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
orderedSetAggregate = false;
|
||||||
|
}
|
||||||
visitOverClause(
|
visitOverClause(
|
||||||
over.getPartitions(),
|
over.getPartitions(),
|
||||||
over.getOrderList(),
|
over.getOrderList(),
|
||||||
|
@ -3252,7 +3262,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
over.getStartExpression(),
|
over.getStartExpression(),
|
||||||
over.getEndKind(),
|
over.getEndKind(),
|
||||||
over.getEndExpression(),
|
over.getEndExpression(),
|
||||||
over.getExclusion()
|
over.getExclusion(),
|
||||||
|
orderedSetAggregate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3267,7 +3278,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
null,
|
null,
|
||||||
Over.FrameKind.CURRENT_ROW,
|
Over.FrameKind.CURRENT_ROW,
|
||||||
null,
|
null,
|
||||||
Over.FrameExclusion.NO_OTHERS
|
Over.FrameExclusion.NO_OTHERS,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3279,12 +3291,15 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
||||||
Expression startExpression,
|
Expression startExpression,
|
||||||
Over.FrameKind endKind,
|
Over.FrameKind endKind,
|
||||||
Expression endExpression,
|
Expression endExpression,
|
||||||
Over.FrameExclusion exclusion) {
|
Over.FrameExclusion exclusion,
|
||||||
|
boolean orderedSetAggregate) {
|
||||||
try {
|
try {
|
||||||
clauseStack.push( Clause.OVER );
|
clauseStack.push( Clause.OVER );
|
||||||
appendSql( " over(" );
|
appendSql( " over(" );
|
||||||
visitPartitionByClause( partitionExpressions );
|
visitPartitionByClause( partitionExpressions );
|
||||||
renderOrderBy( !partitionExpressions.isEmpty(), sortSpecifications );
|
if ( !orderedSetAggregate ) {
|
||||||
|
renderOrderBy( !partitionExpressions.isEmpty(), sortSpecifications );
|
||||||
|
}
|
||||||
if ( mode == Over.FrameMode.ROWS && startKind == Over.FrameKind.UNBOUNDED_PRECEDING && endKind == Over.FrameKind.CURRENT_ROW && exclusion == Over.FrameExclusion.NO_OTHERS ) {
|
if ( mode == Over.FrameMode.ROWS && startKind == Over.FrameKind.UNBOUNDED_PRECEDING && endKind == Over.FrameKind.CURRENT_ROW && exclusion == Over.FrameExclusion.NO_OTHERS ) {
|
||||||
// This is the default, so we don't need to render anything
|
// This is the default, so we don't need to render anything
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,7 @@ public class AbstractSqlAstWalker implements SqlAstWalker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOver(Over over) {
|
public void visitOver(Over<?> over) {
|
||||||
over.getExpression().accept( this );
|
over.getExpression().accept( this );
|
||||||
for ( Expression partition : over.getPartitions() ) {
|
for ( Expression partition : over.getPartitions() ) {
|
||||||
partition.accept( this );
|
partition.accept( this );
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class AggregateFunctionChecker extends AbstractSqlAstWalker {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitOver(Over over) {
|
public void visitOver(Over<?> over) {
|
||||||
// Only need to visit the expression over which the window is created as the window definition can't have aggregates
|
// Only need to visit the expression over which the window is created as the window definition can't have aggregates
|
||||||
// If the expression is an aggregate function, this means the aggregate is used as window function, which is fine
|
// If the expression is an aggregate function, this means the aggregate is used as window function, which is fine
|
||||||
// We only care about actually aggregating functions, which might be an argument of this function though
|
// We only care about actually aggregating functions, which might be an argument of this function though
|
||||||
|
|
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* 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.sql.ast.spi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.persister.internal.SqlFragmentPredicate;
|
||||||
|
import org.hibernate.query.sqm.tree.expression.Conversion;
|
||||||
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Any;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.CastTarget;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Collation;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ConvertedQueryLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Distinct;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Duration;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.DurationUnit;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.EntityTypeLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Every;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ExtractUnit;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Format;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Over;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Overflow;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Star;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.TrimSpecification;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.UnaryOperation;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.BetweenPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.GroupedPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Junction;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.LikePredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.NegatedPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A walker that allows to replace expressions.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class ExpressionReplacementWalker implements SqlAstWalker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To avoid introducing a walker/visitor that returns an object,
|
||||||
|
* we use a heap variable to transfer the return value.
|
||||||
|
*/
|
||||||
|
private SqlAstNode returnedNode;
|
||||||
|
|
||||||
|
public final <X extends SqlAstNode> X replaceExpressions(X expression) {
|
||||||
|
expression.accept( this );
|
||||||
|
//noinspection unchecked
|
||||||
|
return (X) returnedNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <X extends SqlAstNode> X replaceExpression(X expression) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doReplaceExpression(SqlAstNode expression) {
|
||||||
|
returnedNode = replaceExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitColumnReference(ColumnReference columnReference) {
|
||||||
|
doReplaceExpression( columnReference );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitExtractUnit(ExtractUnit extractUnit) {
|
||||||
|
doReplaceExpression( extractUnit );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFormat(Format format) {
|
||||||
|
doReplaceExpression( format );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitDistinct(Distinct distinct) {
|
||||||
|
doReplaceExpression( distinct );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOverflow(Overflow overflow) {
|
||||||
|
doReplaceExpression( overflow );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitStar(Star star) {
|
||||||
|
doReplaceExpression( star );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTrimSpecification(TrimSpecification trimSpecification) {
|
||||||
|
doReplaceExpression( trimSpecification );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCastTarget(CastTarget castTarget) {
|
||||||
|
doReplaceExpression( castTarget );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
|
||||||
|
doReplaceExpression( arithmeticExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCaseSearchedExpression(CaseSearchedExpression caseSearchedExpression) {
|
||||||
|
doReplaceExpression( caseSearchedExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCaseSimpleExpression(CaseSimpleExpression caseSimpleExpression) {
|
||||||
|
doReplaceExpression( caseSimpleExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitAny(Any any) {
|
||||||
|
doReplaceExpression( any );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEvery(Every every) {
|
||||||
|
doReplaceExpression( every );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSummarization(Summarization every) {
|
||||||
|
doReplaceExpression( every );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOver(Over<?> over) {
|
||||||
|
doReplaceExpression( over );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSelfRenderingExpression(SelfRenderingExpression expression) {
|
||||||
|
doReplaceExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlSelectionExpression(SqlSelectionExpression expression) {
|
||||||
|
doReplaceExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEntityTypeLiteral(EntityTypeLiteral expression) {
|
||||||
|
doReplaceExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTuple(SqlTuple tuple) {
|
||||||
|
doReplaceExpression( tuple );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitCollation(Collation collation) {
|
||||||
|
doReplaceExpression( collation );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitParameter(JdbcParameter jdbcParameter) {
|
||||||
|
doReplaceExpression( jdbcParameter );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitJdbcLiteral(JdbcLiteral<?> jdbcLiteral) {
|
||||||
|
doReplaceExpression( jdbcLiteral );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQueryLiteral(QueryLiteral<?> queryLiteral) {
|
||||||
|
doReplaceExpression( queryLiteral );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void acceptConvertedQueryLiteral(ConvertedQueryLiteral<?, ?> convertedQueryLiteral) {
|
||||||
|
doReplaceExpression( convertedQueryLiteral );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitUnaryOperationExpression(UnaryOperation unaryOperationExpression) {
|
||||||
|
doReplaceExpression( unaryOperationExpression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitModifiedSubQueryExpression(ModifiedSubQueryExpression expression) {
|
||||||
|
doReplaceExpression( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitDurationUnit(DurationUnit durationUnit) {
|
||||||
|
doReplaceExpression( durationUnit );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitDuration(Duration duration) {
|
||||||
|
doReplaceExpression( duration );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitConversion(Conversion conversion) {
|
||||||
|
doReplaceExpression( conversion );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
|
doReplaceExpression( booleanExpressionPredicate );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlFragmentPredicate(SqlFragmentPredicate predicate) {
|
||||||
|
doReplaceExpression( predicate );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBetweenPredicate(BetweenPredicate betweenPredicate) {
|
||||||
|
final Expression expression = replaceExpression( betweenPredicate.getExpression() );
|
||||||
|
final Expression lowerBound = replaceExpression( betweenPredicate.getLowerBound() );
|
||||||
|
final Expression upperBound = replaceExpression( betweenPredicate.getUpperBound() );
|
||||||
|
if ( expression != betweenPredicate.getExpression()
|
||||||
|
|| lowerBound != betweenPredicate.getLowerBound()
|
||||||
|
|| upperBound != betweenPredicate.getUpperBound() ) {
|
||||||
|
returnedNode = new BetweenPredicate(
|
||||||
|
expression,
|
||||||
|
lowerBound,
|
||||||
|
upperBound,
|
||||||
|
betweenPredicate.isNegated(),
|
||||||
|
betweenPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = betweenPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitGroupedPredicate(GroupedPredicate groupedPredicate) {
|
||||||
|
groupedPredicate.getSubPredicate().accept( this );
|
||||||
|
if ( returnedNode != groupedPredicate.getSubPredicate() ) {
|
||||||
|
returnedNode = new GroupedPredicate( (Predicate) returnedNode );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = groupedPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitInListPredicate(InListPredicate inListPredicate) {
|
||||||
|
final Expression testExpression = replaceExpression( inListPredicate.getTestExpression() );
|
||||||
|
List<Expression> items = null;
|
||||||
|
final List<Expression> listExpressions = inListPredicate.getListExpressions();
|
||||||
|
for ( int i = 0; i < listExpressions.size(); i++ ) {
|
||||||
|
final Expression listExpression = listExpressions.get( i );
|
||||||
|
final Expression newListExpression = replaceExpression( listExpression );
|
||||||
|
if ( newListExpression != listExpression ) {
|
||||||
|
if ( items == null ) {
|
||||||
|
items = new ArrayList<>( listExpressions );
|
||||||
|
}
|
||||||
|
items.set( i, newListExpression );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( testExpression != inListPredicate.getTestExpression() || items != null ) {
|
||||||
|
returnedNode = new InListPredicate(
|
||||||
|
testExpression,
|
||||||
|
items == null ? listExpressions : items,
|
||||||
|
inListPredicate.isNegated(),
|
||||||
|
inListPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = inListPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitInSubQueryPredicate(InSubQueryPredicate inSubQueryPredicate) {
|
||||||
|
final Expression testExpression = replaceExpression( inSubQueryPredicate.getTestExpression() );
|
||||||
|
final QueryPart subQuery = replaceExpression( inSubQueryPredicate.getSubQuery() );
|
||||||
|
if ( testExpression != inSubQueryPredicate.getTestExpression()
|
||||||
|
|| subQuery != inSubQueryPredicate.getSubQuery() ) {
|
||||||
|
returnedNode = new InSubQueryPredicate(
|
||||||
|
testExpression,
|
||||||
|
subQuery,
|
||||||
|
inSubQueryPredicate.isNegated(),
|
||||||
|
inSubQueryPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = inSubQueryPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitExistsPredicate(ExistsPredicate existsPredicate) {
|
||||||
|
final QueryPart queryPart = replaceExpression( existsPredicate.getExpression() );
|
||||||
|
if ( queryPart != existsPredicate.getExpression() ) {
|
||||||
|
returnedNode = new ExistsPredicate(
|
||||||
|
queryPart,
|
||||||
|
existsPredicate.isNegated(),
|
||||||
|
existsPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = existsPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitJunction(Junction junction) {
|
||||||
|
final List<Predicate> predicates = junction.getPredicates();
|
||||||
|
List<Predicate> newPredicates = null;
|
||||||
|
for ( int i = 0; i < predicates.size(); i++ ) {
|
||||||
|
predicates.get( i ).accept( this );
|
||||||
|
if ( returnedNode != predicates.get( i ) ) {
|
||||||
|
if ( newPredicates == null ) {
|
||||||
|
newPredicates = new ArrayList<>( predicates );
|
||||||
|
}
|
||||||
|
newPredicates.set( i, (Predicate) returnedNode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( newPredicates != null ) {
|
||||||
|
returnedNode = new Junction(
|
||||||
|
junction.getNature(),
|
||||||
|
newPredicates,
|
||||||
|
junction.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = junction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitLikePredicate(LikePredicate likePredicate) {
|
||||||
|
final Expression matchExpression = replaceExpression( likePredicate.getMatchExpression() );
|
||||||
|
final Expression patternExpression = replaceExpression( likePredicate.getPattern() );
|
||||||
|
final Expression escapeExpression = likePredicate.getEscapeCharacter() == null
|
||||||
|
? null
|
||||||
|
: replaceExpression( likePredicate.getEscapeCharacter() );
|
||||||
|
if ( matchExpression != likePredicate.getMatchExpression()
|
||||||
|
|| patternExpression != likePredicate.getPattern()
|
||||||
|
|| escapeExpression != likePredicate.getEscapeCharacter() ) {
|
||||||
|
returnedNode = new LikePredicate(
|
||||||
|
matchExpression,
|
||||||
|
patternExpression,
|
||||||
|
escapeExpression,
|
||||||
|
likePredicate.isNegated(),
|
||||||
|
likePredicate.isCaseSensitive(),
|
||||||
|
likePredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = likePredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitNegatedPredicate(NegatedPredicate negatedPredicate) {
|
||||||
|
negatedPredicate.getPredicate().accept( this );
|
||||||
|
if ( returnedNode != negatedPredicate.getPredicate() ) {
|
||||||
|
returnedNode = new NegatedPredicate( (Predicate) returnedNode );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = negatedPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitNullnessPredicate(NullnessPredicate nullnessPredicate) {
|
||||||
|
final Expression expression = replaceExpression( nullnessPredicate.getExpression() );
|
||||||
|
if ( expression != nullnessPredicate.getExpression() ) {
|
||||||
|
returnedNode = new NullnessPredicate(
|
||||||
|
expression,
|
||||||
|
nullnessPredicate.isNegated(),
|
||||||
|
nullnessPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = nullnessPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitRelationalPredicate(ComparisonPredicate comparisonPredicate) {
|
||||||
|
final Expression lhs = replaceExpression( comparisonPredicate.getLeftHandExpression() );
|
||||||
|
final Expression rhs = replaceExpression( comparisonPredicate.getRightHandExpression() );
|
||||||
|
if ( lhs != comparisonPredicate.getLeftHandExpression()
|
||||||
|
|| rhs != comparisonPredicate.getRightHandExpression() ) {
|
||||||
|
returnedNode = new ComparisonPredicate(
|
||||||
|
lhs,
|
||||||
|
comparisonPredicate.getOperator(),
|
||||||
|
rhs,
|
||||||
|
comparisonPredicate.getExpressionType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = comparisonPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSelfRenderingPredicate(SelfRenderingPredicate selfRenderingPredicate) {
|
||||||
|
final SelfRenderingExpression selfRenderingExpression = replaceExpression( selfRenderingPredicate.getSelfRenderingExpression() );
|
||||||
|
if ( selfRenderingExpression != selfRenderingPredicate.getSelfRenderingExpression() ) {
|
||||||
|
returnedNode = new SelfRenderingPredicate(
|
||||||
|
selfRenderingExpression
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
returnedNode = selfRenderingPredicate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unsupported */
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSelectStatement(SelectStatement statement) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitDeleteStatement(DeleteStatement statement) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitUpdateStatement(UpdateStatement statement) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitInsertStatement(InsertStatement statement) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitAssignment(Assignment assignment) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQuerySpec(QuerySpec querySpec) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSortSpecification(SortSpecification sortSpecification) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOffsetFetchClause(QueryPart querySpec) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSelectClause(SelectClause selectClause) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitSqlSelection(SqlSelection sqlSelection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFromClause(FromClause fromClause) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTableGroup(TableGroup tableGroup) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTableGroupJoin(TableGroupJoin tableGroupJoin) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitNamedTableReference(NamedTableReference tableReference) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitValuesTableReference(ValuesTableReference tableReference) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQueryPartTableReference(QueryPartTableReference tableReference) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitTableReferenceJoin(TableReferenceJoin tableReferenceJoin) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFilterPredicate(FilterPredicate filterPredicate) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFilterFragmentPredicate(FilterPredicate.FilterFragmentPredicate fragmentPredicate) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,13 +9,19 @@ package org.hibernate.sql.ast.tree.expression;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||||
import org.hibernate.sql.ast.SqlAstWalker;
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationState;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
|
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Christian Beikov
|
* @author Christian Beikov
|
||||||
*/
|
*/
|
||||||
public class Over implements Expression {
|
public class Over<T> implements Expression, DomainResultProducer<T> {
|
||||||
|
|
||||||
private final Expression expression;
|
private final Expression expression;
|
||||||
private final List<Expression> partitions;
|
private final List<Expression> partitions;
|
||||||
|
@ -109,6 +115,29 @@ public class Over implements Expression {
|
||||||
walker.visitOver( this );
|
walker.visitOver( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DomainResult<T> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
|
||||||
|
final SqlSelection sqlSelection = createSelection( creationState.getSqlAstCreationState() );
|
||||||
|
return new BasicResult(
|
||||||
|
sqlSelection.getValuesArrayPosition(),
|
||||||
|
resultVariable,
|
||||||
|
expression.getExpressionType().getJdbcMappings().get( 0 ).getMappedJavaType()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applySqlSelections(DomainResultCreationState creationState) {
|
||||||
|
createSelection( creationState.getSqlAstCreationState() );
|
||||||
|
}
|
||||||
|
|
||||||
|
private SqlSelection createSelection(SqlAstCreationState creationState) {
|
||||||
|
return creationState.getSqlExpressionResolver().resolveSqlSelection(
|
||||||
|
this,
|
||||||
|
expression.getExpressionType().getJdbcMappings().get( 0 ).getMappedJavaType(),
|
||||||
|
creationState.getCreationContext().getSessionFactory().getTypeConfiguration()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static enum FrameMode {
|
public static enum FrameMode {
|
||||||
ROWS,
|
ROWS,
|
||||||
RANGE,
|
RANGE,
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class Junction implements Predicate {
|
||||||
|
|
||||||
private final Nature nature;
|
private final Nature nature;
|
||||||
private final JdbcMappingContainer expressionType;
|
private final JdbcMappingContainer expressionType;
|
||||||
private final List<Predicate> predicates = new ArrayList<>();
|
private final List<Predicate> predicates;
|
||||||
|
|
||||||
public Junction() {
|
public Junction() {
|
||||||
this( Nature.CONJUNCTION );
|
this( Nature.CONJUNCTION );
|
||||||
|
@ -42,6 +42,16 @@ public class Junction implements Predicate {
|
||||||
public Junction(Nature nature, JdbcMappingContainer expressionType) {
|
public Junction(Nature nature, JdbcMappingContainer expressionType) {
|
||||||
this.nature = nature;
|
this.nature = nature;
|
||||||
this.expressionType = expressionType;
|
this.expressionType = expressionType;
|
||||||
|
this.predicates = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Junction(
|
||||||
|
Nature nature,
|
||||||
|
List<Predicate> predicates,
|
||||||
|
JdbcMappingContainer expressionType) {
|
||||||
|
this.nature = nature;
|
||||||
|
this.expressionType = expressionType;
|
||||||
|
this.predicates = predicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(Predicate predicate) {
|
public void add(Predicate predicate) {
|
||||||
|
|
|
@ -34,6 +34,15 @@ public abstract class QueryPart implements SqlAstNode, Expression, DomainResultP
|
||||||
this.isRoot = isRoot;
|
this.isRoot = isRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected QueryPart(boolean isRoot, QueryPart original) {
|
||||||
|
this.isRoot = isRoot;
|
||||||
|
this.hasPositionalSortItem = original.hasPositionalSortItem;
|
||||||
|
this.sortSpecifications = original.sortSpecifications;
|
||||||
|
this.offsetClauseExpression = original.offsetClauseExpression;
|
||||||
|
this.fetchClauseExpression = original.fetchClauseExpression;
|
||||||
|
this.fetchClauseType = original.fetchClauseType;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract QuerySpec getFirstQuerySpec();
|
public abstract QuerySpec getFirstQuerySpec();
|
||||||
|
|
||||||
public abstract QuerySpec getLastQuerySpec();
|
public abstract QuerySpec getLastQuerySpec();
|
||||||
|
@ -100,7 +109,7 @@ public abstract class QueryPart implements SqlAstNode, Expression, DomainResultP
|
||||||
public void setFetchClauseExpression(Expression fetchClauseExpression, FetchClauseType fetchClauseType) {
|
public void setFetchClauseExpression(Expression fetchClauseExpression, FetchClauseType fetchClauseType) {
|
||||||
if ( fetchClauseExpression == null ) {
|
if ( fetchClauseExpression == null ) {
|
||||||
this.fetchClauseExpression = null;
|
this.fetchClauseExpression = null;
|
||||||
this.fetchClauseType = null;
|
this.fetchClauseType = FetchClauseType.ROWS_ONLY;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( fetchClauseType == null ) {
|
if ( fetchClauseType == null ) {
|
||||||
|
|
|
@ -35,22 +35,36 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContainer, Expression, DomainResultProducer {
|
public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContainer, Expression, DomainResultProducer {
|
||||||
|
|
||||||
private final FromClause fromClause;
|
private final FromClause fromClause;
|
||||||
private final SelectClause selectClause = new SelectClause();
|
private final SelectClause selectClause;
|
||||||
|
|
||||||
private Predicate whereClauseRestrictions;
|
private Predicate whereClauseRestrictions;
|
||||||
|
|
||||||
private boolean hasPositionalGroupItem;
|
|
||||||
private List<Expression> groupByClauseExpressions = Collections.emptyList();
|
private List<Expression> groupByClauseExpressions = Collections.emptyList();
|
||||||
private Predicate havingClauseRestrictions;
|
private Predicate havingClauseRestrictions;
|
||||||
|
|
||||||
public QuerySpec(boolean isRoot) {
|
public QuerySpec(boolean isRoot) {
|
||||||
super( isRoot );
|
super( isRoot );
|
||||||
this.fromClause = new FromClause();
|
this.fromClause = new FromClause();
|
||||||
|
this.selectClause = new SelectClause();
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuerySpec(boolean isRoot, int expectedNumberOfRoots) {
|
public QuerySpec(boolean isRoot, int expectedNumberOfRoots) {
|
||||||
super( isRoot );
|
super( isRoot );
|
||||||
this.fromClause = new FromClause( expectedNumberOfRoots );
|
this.fromClause = new FromClause( expectedNumberOfRoots );
|
||||||
|
this.selectClause = new SelectClause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private QuerySpec(QuerySpec original) {
|
||||||
|
super( false, original );
|
||||||
|
this.fromClause = original.fromClause;
|
||||||
|
this.selectClause = original.selectClause;
|
||||||
|
this.whereClauseRestrictions = original.whereClauseRestrictions;
|
||||||
|
this.groupByClauseExpressions = original.groupByClauseExpressions;
|
||||||
|
this.havingClauseRestrictions = original.havingClauseRestrictions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuerySpec asSubQuery() {
|
||||||
|
return isRoot() ? new QuerySpec( this ) : this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -94,20 +108,8 @@ public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContain
|
||||||
return groupByClauseExpressions;
|
return groupByClauseExpressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPositionalGroupItem() {
|
|
||||||
return hasPositionalGroupItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupByClauseExpressions(List<Expression> groupByClauseExpressions) {
|
public void setGroupByClauseExpressions(List<Expression> groupByClauseExpressions) {
|
||||||
this.groupByClauseExpressions = groupByClauseExpressions == null ? Collections.emptyList() : groupByClauseExpressions;
|
this.groupByClauseExpressions = groupByClauseExpressions == null ? Collections.emptyList() : groupByClauseExpressions;
|
||||||
if ( isRoot() ) {
|
|
||||||
for ( int i = 0; i < groupByClauseExpressions.size(); i++ ) {
|
|
||||||
final Expression groupItem = groupByClauseExpressions.get( i );
|
|
||||||
if ( groupItem instanceof SqmAliasedNodeRef ) {
|
|
||||||
hasPositionalGroupItem = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Predicate getHavingClauseRestrictions() {
|
public Predicate getHavingClauseRestrictions() {
|
||||||
|
|
|
@ -10,9 +10,6 @@ import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.dialect.AbstractHANADialect;
|
|
||||||
import org.hibernate.dialect.SQLServerDialect;
|
|
||||||
|
|
||||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
import org.hibernate.testing.orm.domain.gambit.EntityOfBasics;
|
||||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||||
|
@ -21,11 +18,11 @@ import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Tuple;
|
||||||
import jakarta.persistence.TypedQuery;
|
import jakarta.persistence.TypedQuery;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -141,7 +138,6 @@ public class OrderedSetAggregateTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsInverseDistributionFunctions.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsInverseDistributionFunctions.class)
|
||||||
@SkipForDialect(dialectClass = SQLServerDialect.class, reason = "The function is only supported as window function and needs emulation")
|
|
||||||
public void testInverseDistribution(SessionFactoryScope scope) {
|
public void testInverseDistribution(SessionFactoryScope scope) {
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
|
@ -153,9 +149,7 @@ public class OrderedSetAggregateTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
|
||||||
@SkipForDialect(dialectClass = SQLServerDialect.class, reason = "The function is only supported as window function and needs emulation")
|
public void testHypotheticalSetPercentRank(SessionFactoryScope scope) {
|
||||||
@SkipForDialect(dialectClass = AbstractHANADialect.class, matchSubTypes = true, reason = "The function is only supported as window function and needs emulation")
|
|
||||||
public void testHypotheticalSet(SessionFactoryScope scope) {
|
|
||||||
scope.inTransaction(
|
scope.inTransaction(
|
||||||
session -> {
|
session -> {
|
||||||
TypedQuery<Double> q = session.createQuery( "select percent_rank(5) within group (order by eob.theInt asc) from EntityOfBasics eob", Double.class );
|
TypedQuery<Double> q = session.createQuery( "select percent_rank(5) within group (order by eob.theInt asc) from EntityOfBasics eob", Double.class );
|
||||||
|
@ -163,4 +157,30 @@ public class OrderedSetAggregateTest {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
|
||||||
|
public void testHypotheticalSetRank(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
TypedQuery<Long> q = session.createQuery( "select rank(5) within group (order by eob.theInt asc) from EntityOfBasics eob", Long.class );
|
||||||
|
assertEquals( 1L, q.getSingleResult() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsHypotheticalSetFunctions.class)
|
||||||
|
public void testHypotheticalSetRankWithGroupByHavingOrderByLimit(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
TypedQuery<Tuple> q = session.createQuery( "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", Tuple.class );
|
||||||
|
List<Tuple> resultList = q.getResultList();
|
||||||
|
assertEquals( 3, resultList.size() );
|
||||||
|
assertEquals( 1L, resultList.get( 0 ).get( 1, Long.class ) );
|
||||||
|
assertEquals( 1L, resultList.get( 1 ).get( 1, Long.class ) );
|
||||||
|
assertEquals( 1L, resultList.get( 2 ).get( 1, Long.class ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue