HHH-7165 - count() query on classes using EmbeddedId should not use id column tuple on Dialects which dont support non-distinct tuple counts
This commit is contained in:
parent
41dd7079e0
commit
c3fc7f3214
|
@ -58,10 +58,14 @@ tokens
|
||||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, HqlSqlBaseWalker.class.getName());
|
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, HqlSqlBaseWalker.class.getName());
|
||||||
|
|
||||||
private int level = 0;
|
private int level = 0;
|
||||||
|
|
||||||
private boolean inSelect = false;
|
private boolean inSelect = false;
|
||||||
private boolean inFunctionCall = false;
|
private boolean inFunctionCall = false;
|
||||||
private boolean inCase = false;
|
private boolean inCase = false;
|
||||||
private boolean inFrom = false;
|
private boolean inFrom = false;
|
||||||
|
private boolean inCount = false;
|
||||||
|
private boolean inCountDistinct = false;
|
||||||
|
|
||||||
private int statementType;
|
private int statementType;
|
||||||
private String statementTypeName;
|
private String statementTypeName;
|
||||||
// Note: currentClauseType tracks the current clause within the current
|
// Note: currentClauseType tracks the current clause within the current
|
||||||
|
@ -92,6 +96,14 @@ tokens
|
||||||
return inCase;
|
return inCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final boolean isInCount() {
|
||||||
|
return inCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean isInCountDistinct() {
|
||||||
|
return inCountDistinct;
|
||||||
|
}
|
||||||
|
|
||||||
public final int getStatementType() {
|
public final int getStatementType() {
|
||||||
return statementType;
|
return statementType;
|
||||||
}
|
}
|
||||||
|
@ -392,8 +404,11 @@ selectExpr
|
||||||
;
|
;
|
||||||
|
|
||||||
count
|
count
|
||||||
: #(COUNT ( DISTINCT | ALL )? ( aggregateExpr | ROW_STAR ) )
|
: #(COUNT { inCount = true; } ( DISTINCT { inCountDistinct = true; } | ALL )? ( aggregateExpr | ROW_STAR ) ) {
|
||||||
;
|
inCount = false;
|
||||||
|
inCountDistinct = false;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
constructor
|
constructor
|
||||||
{ String className = null; }
|
{ String className = null; }
|
||||||
|
|
|
@ -41,7 +41,8 @@ public class ComponentJoin extends FromElement {
|
||||||
private final ComponentType componentType;
|
private final ComponentType componentType;
|
||||||
|
|
||||||
private final String componentProperty;
|
private final String componentProperty;
|
||||||
private final String columns;
|
private final String[] columns;
|
||||||
|
private final String columnsFragment;
|
||||||
|
|
||||||
public ComponentJoin(
|
public ComponentJoin(
|
||||||
FromClause fromClause,
|
FromClause fromClause,
|
||||||
|
@ -56,16 +57,16 @@ public class ComponentJoin extends FromElement {
|
||||||
fromClause.addJoinByPathMap( componentPath, this );
|
fromClause.addJoinByPathMap( componentPath, this );
|
||||||
initializeComponentJoin( new ComponentFromElementType( this ) );
|
initializeComponentJoin( new ComponentFromElementType( this ) );
|
||||||
|
|
||||||
final String[] cols = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty );
|
this.columns = origin.getPropertyMapping( "" ).toColumns( getTableAlias(), componentProperty );
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
for ( int j = 0; j < cols.length; j++ ) {
|
for ( int j = 0; j < columns.length; j++ ) {
|
||||||
final String column = cols[j];
|
final String column = columns[j];
|
||||||
if ( j > 0 ) {
|
if ( j > 0 ) {
|
||||||
buf.append( ", " );
|
buf.append( ", " );
|
||||||
}
|
}
|
||||||
buf.append( column );
|
buf.append( column );
|
||||||
}
|
}
|
||||||
this.columns = buf.toString();
|
this.columnsFragment = buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getComponentPath() {
|
public String getComponentPath() {
|
||||||
|
@ -86,9 +87,6 @@ public class ComponentJoin extends FromElement {
|
||||||
return getComponentType();
|
return getComponentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getIdentityColumn() {
|
public String getIdentityColumn() {
|
||||||
// used to "resolve" the IdentNode when our alias is encountered *by itself* in the query; so
|
// used to "resolve" the IdentNode when our alias is encountered *by itself* in the query; so
|
||||||
|
@ -96,12 +94,14 @@ public class ComponentJoin extends FromElement {
|
||||||
// NOTE : ^^ is true *except for* when encountered by itself in the SELECT clause. That gets
|
// NOTE : ^^ is true *except for* when encountered by itself in the SELECT clause. That gets
|
||||||
// routed through org.hibernate.hql.internal.ast.tree.ComponentJoin.ComponentFromElementType.renderScalarIdentifierSelect()
|
// routed through org.hibernate.hql.internal.ast.tree.ComponentJoin.ComponentFromElementType.renderScalarIdentifierSelect()
|
||||||
// which we also override to account for
|
// which we also override to account for
|
||||||
|
return columnsFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getIdentityColumns() {
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getDisplayText() {
|
public String getDisplayText() {
|
||||||
return "ComponentJoin{path=" + getComponentPath() + ", type=" + componentType.getReturnedClass() + "}";
|
return "ComponentJoin{path=" + getComponentPath() + ", type=" + componentType.getReturnedClass() + "}";
|
||||||
|
|
|
@ -325,13 +325,23 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIdentityColumn() {
|
public String getIdentityColumn() {
|
||||||
|
final String[] cols = getIdentityColumns();
|
||||||
|
if ( cols.length == 1 ) {
|
||||||
|
return cols[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "(" + StringHelper.join( ", ", cols ) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getIdentityColumns() {
|
||||||
checkInitialized();
|
checkInitialized();
|
||||||
String table = getTableAlias();
|
final String table = getTableAlias();
|
||||||
if ( table == null ) {
|
if ( table == null ) {
|
||||||
throw new IllegalStateException( "No table alias for node " + this );
|
throw new IllegalStateException( "No table alias for node " + this );
|
||||||
}
|
}
|
||||||
String[] cols;
|
|
||||||
String propertyName;
|
final String propertyName;
|
||||||
if ( getEntityPersister() != null && getEntityPersister().getEntityMetamodel() != null
|
if ( getEntityPersister() != null && getEntityPersister().getEntityMetamodel() != null
|
||||||
&& getEntityPersister().getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
|
&& getEntityPersister().getEntityMetamodel().hasNonIdentifierPropertyNamedId() ) {
|
||||||
propertyName = getEntityPersister().getIdentifierPropertyName();
|
propertyName = getEntityPersister().getIdentifierPropertyName();
|
||||||
|
@ -339,14 +349,13 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
||||||
else {
|
else {
|
||||||
propertyName = EntityPersister.ENTITY_ID;
|
propertyName = EntityPersister.ENTITY_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( getWalker().getStatementType() == HqlSqlTokenTypes.SELECT ) {
|
if ( getWalker().getStatementType() == HqlSqlTokenTypes.SELECT ) {
|
||||||
cols = getPropertyMapping( propertyName ).toColumns( table, propertyName );
|
return getPropertyMapping( propertyName ).toColumns( table, propertyName );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cols = getPropertyMapping( propertyName ).toColumns( propertyName );
|
return getPropertyMapping( propertyName ).toColumns( propertyName );
|
||||||
}
|
}
|
||||||
String result = StringHelper.join( ", ", cols );
|
|
||||||
return cols.length == 1 ? result : "(" + result + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCollectionJoin(boolean collectionJoin) {
|
public void setCollectionJoin(boolean collectionJoin) {
|
||||||
|
|
|
@ -149,11 +149,25 @@ public class IdentNode extends FromReferenceNode implements SelectExpression {
|
||||||
|
|
||||||
private boolean resolveAsAlias() {
|
private boolean resolveAsAlias() {
|
||||||
// This is not actually a constant, but a reference to FROM element.
|
// This is not actually a constant, but a reference to FROM element.
|
||||||
FromElement element = getWalker().getCurrentFromClause().getFromElement(getText());
|
FromElement element = getWalker().getCurrentFromClause().getFromElement( getText() );
|
||||||
if (element != null) {
|
if ( element != null ) {
|
||||||
setFromElement(element);
|
setType( SqlTokenTypes.ALIAS_REF );
|
||||||
setText(element.getIdentityColumn());
|
setFromElement( element );
|
||||||
setType(SqlTokenTypes.ALIAS_REF);
|
String[] columnExpressions = element.getIdentityColumns();
|
||||||
|
final boolean isInNonDistinctCount = getWalker().isInCount() && ! getWalker().isInCountDistinct();
|
||||||
|
final boolean isCompositePk = columnExpressions.length > 1;
|
||||||
|
if ( isCompositePk
|
||||||
|
&& isInNonDistinctCount
|
||||||
|
&& ! getWalker().getSessionFactoryHelper().getFactory().getDialect().supportsTupleCounts() ) {
|
||||||
|
setText( columnExpressions[0] );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
String joinedFragment = StringHelper.join( ", ", columnExpressions );
|
||||||
|
if ( ! getWalker().isInCount() ) {
|
||||||
|
joinedFragment = "(" + joinedFragment + ")";
|
||||||
|
}
|
||||||
|
setText( joinedFragment );
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -74,6 +74,27 @@ public class CompositeIdTest extends BaseCoreFunctionalTestCase {
|
||||||
assertEquals( expectCommas, hadCommas );
|
assertEquals( expectCommas, hadCommas );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDistinctCountOfEntityWithCompositeId() {
|
||||||
|
// today we do not account for Dialects supportsTupleDistinctCounts() is false. though really the only
|
||||||
|
// "option" there is to throw an error.
|
||||||
|
final HQLQueryPlan plan = sessionFactory().getQueryPlanCache().getHQLQueryPlan(
|
||||||
|
"select count(distinct o) from Order o",
|
||||||
|
false,
|
||||||
|
Collections.EMPTY_MAP
|
||||||
|
);
|
||||||
|
assertEquals( 1, plan.getTranslators().length );
|
||||||
|
final QueryTranslator translator = plan.getTranslators()[0];
|
||||||
|
final String generatedSql = translator.getSQLString();
|
||||||
|
System.out.println( "Generated SQL : " + generatedSql );
|
||||||
|
|
||||||
|
final int countExpressionListStart = generatedSql.indexOf( "count(" );
|
||||||
|
final int countExpressionListEnd = generatedSql.indexOf( ")", countExpressionListStart );
|
||||||
|
final String countExpressionFragment = generatedSql.substring( countExpressionListStart+6, countExpressionListEnd+1 );
|
||||||
|
assertTrue( countExpressionFragment.startsWith( "distinct" ) );
|
||||||
|
assertTrue( countExpressionFragment.contains( "," ) );
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testQuery() {
|
public void testQuery() {
|
||||||
Session s = openSession();
|
Session s = openSession();
|
||||||
|
|
Loading…
Reference in New Issue