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:
Steve Ebersole 2012-03-13 12:26:04 -05:00
parent 41dd7079e0
commit c3fc7f3214
5 changed files with 84 additions and 25 deletions

View File

@ -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,7 +404,10 @@ 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

View File

@ -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() + "}";

View File

@ -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) {

View File

@ -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;

View File

@ -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();