Implement tuple count emulation
This commit is contained in:
parent
aa7b5529e9
commit
3ecc602852
|
@ -215,6 +215,11 @@ public class CockroachDialect extends Dialect {
|
|||
return NullOrdering.SMALLEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleCounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresParensForTupleDistinctCounts() {
|
||||
return true;
|
||||
|
|
|
@ -21,7 +21,6 @@ import org.hibernate.dialect.identity.IdentityColumnSupport;
|
|||
import org.hibernate.dialect.pagination.AbstractLimitHandler;
|
||||
import org.hibernate.dialect.pagination.DerbyLimitHandler;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.DerbySequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
|
@ -53,7 +52,6 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
|
|||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDerbyDatabaseImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.DecimalTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.jdbc.SmallIntTypeDescriptor;
|
||||
|
@ -185,7 +183,7 @@ public class DerbyDialect extends Dialect {
|
|||
super.initializeFunctionRegistry( queryEngine );
|
||||
|
||||
// Derby needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
|
||||
CommonFunctionFactory.aggregates( queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
|
||||
CommonFunctionFactory.concat_pipeOperator( queryEngine );
|
||||
CommonFunctionFactory.cot( queryEngine );
|
||||
|
|
|
@ -476,7 +476,7 @@ public abstract class Dialect implements ConversionContext {
|
|||
|
||||
//aggregate functions, supported on every database
|
||||
|
||||
CommonFunctionFactory.aggregates( queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
|
||||
|
||||
//the ANSI SQL-defined aggregate functions any() and every() are only
|
||||
//supported on one database, but can be emulated using sum() and case,
|
||||
|
|
|
@ -46,7 +46,6 @@ import org.hibernate.sql.ast.SqlAstTranslator;
|
|||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||
|
@ -157,7 +156,7 @@ public class H2Dialect extends Dialect {
|
|||
super.initializeFunctionRegistry( queryEngine );
|
||||
|
||||
// H2 needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
|
||||
CommonFunctionFactory.aggregates( queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
CommonFunctionFactory.aggregates( this, queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||
|
||||
CommonFunctionFactory.pi( queryEngine );
|
||||
CommonFunctionFactory.cot( queryEngine );
|
||||
|
@ -416,6 +415,11 @@ public class H2Dialect extends Dialect {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleCounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresParensForTupleDistinctCounts() {
|
||||
return true;
|
||||
|
|
|
@ -650,6 +650,11 @@ public class HSQLDialect extends Dialect {
|
|||
return String.valueOf( bool );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleCounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleDistinctCounts() {
|
||||
// from v. 2.2.9 is added support for COUNT(DISTINCT ...) with multiple arguments
|
||||
|
|
|
@ -494,6 +494,11 @@ public class MySQLDialect extends Dialect {
|
|||
return getMySQLVersion() >= 570;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleCounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUnionAll() {
|
||||
return getMySQLVersion() >= 500;
|
||||
|
|
|
@ -531,6 +531,11 @@ public class PostgreSQLDialect extends Dialect {
|
|||
return "select now()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsTupleCounts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresParensForTupleDistinctCounts() {
|
||||
return true;
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.List;
|
|||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
import org.hibernate.metamodel.model.domain.AllowableFunctionReturnType;
|
||||
|
||||
|
@ -1483,7 +1484,10 @@ public class CommonFunctionFactory {
|
|||
.register();
|
||||
}
|
||||
|
||||
public static void aggregates(QueryEngine queryEngine, SqlAstNodeRenderingMode inferenceArgumentRenderingMode) {
|
||||
public static void aggregates(
|
||||
Dialect dialect,
|
||||
QueryEngine queryEngine,
|
||||
SqlAstNodeRenderingMode inferenceArgumentRenderingMode) {
|
||||
queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "max" )
|
||||
.setArgumentRenderingMode( inferenceArgumentRenderingMode )
|
||||
.setExactArgumentCount( 1 )
|
||||
|
@ -1603,11 +1607,7 @@ public class CommonFunctionFactory {
|
|||
.setExactArgumentCount( 1 )
|
||||
.register();
|
||||
|
||||
queryEngine.getSqmFunctionRegistry().namedAggregateDescriptorBuilder( "count" )
|
||||
.setInvariantType( StandardBasicTypes.LONG )
|
||||
.setExactArgumentCount( 1 )
|
||||
.setArgumentListSignature( "([distinct ]{arg|*})" )
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().register( CountFunction.FUNCTION_NAME, new CountFunction( dialect ) );
|
||||
}
|
||||
|
||||
public static void math(QueryEngine queryEngine) {
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.function.FunctionKind;
|
||||
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.query.sqm.sql.internal.AbstractSqmPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation;
|
||||
import org.hibernate.query.sqm.sql.internal.NonAggregatedCompositeValuedPathInterpretation;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.Distinct;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.NullnessLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
|
||||
import org.hibernate.sql.ast.tree.expression.Star;
|
||||
import org.hibernate.sql.ast.tree.predicate.Junction;
|
||||
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class CountFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||
|
||||
public static final String FUNCTION_NAME = "count";
|
||||
private final Dialect dialect;
|
||||
|
||||
public CountFunction(Dialect dialect) {
|
||||
super(
|
||||
FUNCTION_NAME,
|
||||
FunctionKind.AGGREGATE,
|
||||
StandardArgumentsValidators.exactly( 1 ),
|
||||
StandardFunctionReturnTypeResolvers.invariant( StandardBasicTypes.LONG )
|
||||
);
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SqlAppender sqlAppender, List<SqlAstNode> sqlAstArguments, SqlAstTranslator<?> walker) {
|
||||
render( sqlAppender, sqlAstArguments, null, walker );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<SqlAstNode> sqlAstArguments,
|
||||
Predicate filter,
|
||||
SqlAstTranslator<?> translator) {
|
||||
final boolean caseWrapper = filter != null && !translator.supportsFilterClause();
|
||||
final SqlAstNode arg = sqlAstArguments.get( 0 );
|
||||
final SqlAstNode realArg;
|
||||
sqlAppender.appendSql( "count(" );
|
||||
if ( arg instanceof Distinct ) {
|
||||
sqlAppender.appendSql( "distinct " );
|
||||
final Expression distinctArg = ( (Distinct) arg ).getExpression();
|
||||
// todo (6.0): emulate tuple count distinct if necessary
|
||||
realArg = distinctArg;
|
||||
}
|
||||
else {
|
||||
// If the table group supports inner joins, this means that it is non-optional,
|
||||
// which means we can omit the tuples and instead use count(*)
|
||||
final SqlTuple tuple;
|
||||
if ( ( arg instanceof EntityValuedPathInterpretation<?> || arg instanceof NonAggregatedCompositeValuedPathInterpretation<?> )
|
||||
&& ( (AbstractSqmPathInterpretation<?>) arg ).getTableGroup().canUseInnerJoins() ) {
|
||||
realArg = Star.INSTANCE;
|
||||
}
|
||||
else if ( !dialect.supportsTupleCounts() && ( tuple = SqlTupleContainer.getSqlTuple( arg ) ) != null ) {
|
||||
final List<? extends Expression> expressions = tuple.getExpressions();
|
||||
if ( expressions.size() == 1 ) {
|
||||
realArg = expressions.get( 0 );
|
||||
}
|
||||
else {
|
||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = new ArrayList<>( 1 );
|
||||
final Junction junction = new Junction( Junction.Nature.DISJUNCTION );
|
||||
for ( Expression expression : expressions ) {
|
||||
junction.add( new NullnessPredicate( expression ) );
|
||||
}
|
||||
whenFragments.add(
|
||||
new CaseSearchedExpression.WhenFragment(
|
||||
junction,
|
||||
new NullnessLiteral( StandardBasicTypes.INTEGER )
|
||||
)
|
||||
);
|
||||
realArg = new CaseSearchedExpression(
|
||||
StandardBasicTypes.INTEGER,
|
||||
whenFragments,
|
||||
new QueryLiteral<>( 1, StandardBasicTypes.INTEGER )
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
realArg = arg;
|
||||
}
|
||||
}
|
||||
if ( caseWrapper ) {
|
||||
sqlAppender.appendSql( "case when " );
|
||||
filter.accept( translator );
|
||||
sqlAppender.appendSql( " then " );
|
||||
if ( realArg instanceof Star ) {
|
||||
sqlAppender.appendSql( "1" );
|
||||
}
|
||||
else {
|
||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
||||
}
|
||||
sqlAppender.appendSql( " else null end" );
|
||||
}
|
||||
else {
|
||||
translator.render( realArg, SqlAstNodeRenderingMode.DEFAULT );
|
||||
}
|
||||
sqlAppender.appendSql( ')' );
|
||||
if ( filter != null && !caseWrapper ) {
|
||||
sqlAppender.appendSql( " filter (where " );
|
||||
filter.accept( translator );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getArgumentListSignature() {
|
||||
return "([distinct ]{arg|*})";
|
||||
}
|
||||
|
||||
}
|
|
@ -935,10 +935,6 @@ public class ToOneAttributeMapping
|
|||
if ( !canUseParentTableGroup ) {
|
||||
return false;
|
||||
}
|
||||
// // Special case for resolving the table group for entity valued paths
|
||||
// if ( np == navigablePath ) {
|
||||
// return true;
|
||||
// }
|
||||
NavigablePath path = np.getParent();
|
||||
// Fast path
|
||||
if ( path != null && navigablePath.equals( path ) ) {
|
||||
|
|
|
@ -1835,7 +1835,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
|
|||
return new SqmComparisonPredicate(
|
||||
(SqmExpression<?>) x,
|
||||
ComparisonOperator.LESS_THAN_OR_EQUAL,
|
||||
(SqmExpression<?>) y,
|
||||
value( y, (SqmExpression<?>) x ),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.Set;
|
|||
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
|
||||
|
@ -28,6 +29,11 @@ public interface SqlAstTranslator<T extends JdbcOperation> extends SqlAstWalker
|
|||
*/
|
||||
boolean supportsFilterClause();
|
||||
|
||||
/**
|
||||
* Returns the current query part that is translated.
|
||||
*/
|
||||
QueryPart getCurrentQueryPart();
|
||||
|
||||
/**
|
||||
* Not the best spot for this. Its the table names collected while walking the SQL AST.
|
||||
* Its ok here because the translator is consider a one-time-use. It just needs to be called
|
||||
|
|
|
@ -530,6 +530,11 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
return queryPartStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPart getCurrentQueryPart() {
|
||||
return queryPartStack.getCurrent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T translate(JdbcParameterBindings jdbcParameterBindings, QueryOptions queryOptions) {
|
||||
try {
|
||||
|
|
|
@ -55,18 +55,6 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
// if ( fetched ) {
|
||||
// return true;
|
||||
// }
|
||||
// // We also consider it "fetched" if it contains fetched joins
|
||||
// if ( tableGroupJoins != null ) {
|
||||
// for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) {
|
||||
// if ( tableGroupJoin.getJoinedGroup().isFetched() ) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
return fetched;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ public class EntityGraphAttributeResolutionTest extends BaseEntityManagerFunctio
|
|||
attributeNodes = {
|
||||
@NamedAttributeNode("permissions")
|
||||
})
|
||||
@Table(name = "groups") // Name 'group' not accepted by H2
|
||||
@Table( name = "t_group") // Name 'group' not accepted by H2
|
||||
public static class Group {
|
||||
public static final String ENTITY_GRAPH = "group-with-permissions";
|
||||
|
||||
|
|
Loading…
Reference in New Issue