Message: A new issue has been created in JIRA. --------------------------------------------------------------------- View the issue: http://opensource.atlassian.com/projects/hibernate/secure/ViewIssue.jspa?key=HB-177 Here is an overview of the issue: --------------------------------------------------------------------- Key: HB-177 Summary: (Includes Fix) Return type of aggregate function, sum, when applied over booleans should be an integer, giving the number of true values Type: New Feature Status: Unassigned Priority: Minor Project: Hibernate2 Components: core Versions: 2.1 2.0 final 2.0.1 2.0.2 Assignee: Reporter: Hugh Leather Created: Thu, 10 Jul 2003 3:38 PM Updated: Thu, 10 Jul 2003 3:38 PM Environment: MySql Description: When doing a select which aggregates a sum of booleans, the result is also a boolean. I think this is no so useful and would prefer the number of true values. This is what seems to be returned from the underlying sql, but the information is destroyed by casting to a boolean. The fix I have for this issue is a few small chnges to SelectParser.java. Here is the new file, with my additions marked by comments along the lines of // Begin change ... If anyone things this is worth adding and intends to do so, could you email me aat hug...@ho... and let me know. Thanks, Hugh. //$Id: SelectParser.java,v 1.12 2003/05/07 15:09:05 oneovthafew Exp $ package net.sf.hibernate.hql; import java.sql.Types; import java.util.HashSet; import java.util.Set; import net.sf.hibernate.AssertionFailure; import net.sf.hibernate.Hibernate; import net.sf.hibernate.MappingException; import net.sf.hibernate.QueryException; // Begin change : Hugh Leather 10.07.2003 (See comment at top of file) import net.sf.hibernate.type.BooleanType; // End change : Hugh Leather 10.07.2003 (See comment at top of file) import net.sf.hibernate.type.Type; import net.sf.hibernate.util.StringHelper; /** * Parsers the select clause of a hibernate query, looking * for a table (well, really class) alias. */ // Change : Hugh Leather 10.07.2003 // Several small changes are introduced to ensure that the return type of the // sum aggregate function over booleans gives an integer result. public class SelectParser implements Parser { private static final Set aggregateFunctions = new HashSet(); private static final Set countArguments = new HashSet(); static { aggregateFunctions.add("count"); aggregateFunctions.add("avg"); aggregateFunctions.add("max"); aggregateFunctions.add("min"); aggregateFunctions.add("sum"); countArguments.add("distinct"); countArguments.add("all"); countArguments.add("*"); } private boolean ready; private boolean aggregate; private boolean count; private boolean avg; // Begin change : Hugh Leather 10.07.2003 (See comment at top of file) private boolean sum; // End change : Hugh Leather 10.07.2003 (See comment at top of file) private boolean first; private boolean afterNew; private Class holderClass; private SelectPathExpressionParser pathExpressionParser = new SelectPathExpressionParser(); private PathExpressionParser aggregatePathExpressionParser = new PathExpressionParser(); public void token(String token, QueryTranslator q) throws QueryException { String lctoken = token.toLowerCase(); if (first) { first = false; if ( lctoken.equals("distinct") ) { q.setDistinct(true); return; } else if ( lctoken.equals("all") ) { q.setDistinct(false); return; } } if (afterNew) { afterNew=false; holderClass = q.getImportedClass(token); if (holderClass==null) throw new QueryException("class not found: " + token); q.setHolderClass(holderClass); } else if ( token.equals(StringHelper.COMMA) ) { if (ready) throw new QueryException("alias or expression expected in SELECT"); q.appendScalarSelectToken(StringHelper.COMMA_SPACE); ready=true; } else if ( "new".equals(lctoken) ) { afterNew=true; ready=false; } else if ( StringHelper.OPEN_PAREN.equals(token) ) { if (!aggregate && holderClass!=null && !ready) { //opening paren in new Foo ( ... ) ready=true; } else if (aggregate) { q.appendScalarSelectToken(token); } else { throw new QueryException("aggregate function expected before ( in SELECT"); } ready = true; } else if ( StringHelper.CLOSE_PAREN.equals(token) ) { if (holderClass!=null && !ready) { //closing paren in new Foo ( ... ) } else if (aggregate && ready) { q.appendScalarSelectToken(token); } else { throw new QueryException("( expected before ) in select"); } count=false; aggregate = false; ready = false; } else if ( countArguments.contains(lctoken) ) { if ( !ready || !aggregate ) throw new QueryException( token + " only allowed inside aggregate function in SELECT"); q.appendScalarSelectToken(token); } else if ( aggregateFunctions.contains(lctoken) ) { if (!ready) throw new QueryException(", expected before aggregate function in SELECT: " + token); if ( lctoken.equals("count") ) { q.addSelectScalar(Hibernate.INTEGER); //must be handled differently 'cos of count(*) count = true; } else if ( lctoken.equals("avg") ) { avg = true; } // Begin change : Hugh Leather 10.07.2003 (See comment at top of file) else if( lctoken.equals("sum") ) { sum = true; } // End change : Hugh Leather 10.07.2003 (See comment at top of file) aggregate = true; ready = false; q.appendScalarSelectToken(token); } else if (aggregate) { if (!ready) throw new QueryException("( expected after aggregate function in SELECT"); ParserHelper.parse(aggregatePathExpressionParser, q.unalias(token), ParserHelper.PATH_SEPARATORS, q); if ( aggregatePathExpressionParser.isCollectionValued() ) { q.addCollection( aggregatePathExpressionParser.getCollectionName(), aggregatePathExpressionParser.getCollectionRole() ); } q.appendScalarSelectToken( aggregatePathExpressionParser.getWhereColumn() ); if (!count) q.addSelectScalar( aggregateType( aggregatePathExpressionParser.getWhereColumnType(), q ) ); aggregatePathExpressionParser.addAssociation(q); } else { if (!ready) throw new QueryException(", expected in SELECT"); ParserHelper.parse(pathExpressionParser, q.unalias(token), ParserHelper.PATH_SEPARATORS, q); if ( pathExpressionParser.isCollectionValued() ) { q.addCollection( pathExpressionParser.getCollectionName(), pathExpressionParser.getCollectionRole() ); } else if ( pathExpressionParser.getWhereColumnType().isEntityType() ) { q.addSelectClass( pathExpressionParser.getSelectName() ); } q.appendScalarSelectTokens( pathExpressionParser.getWhereColumns() ); q.addSelectScalar( pathExpressionParser.getWhereColumnType() ); pathExpressionParser.addAssociation(q); ready = false; } } public Type aggregateType(Type type, QueryTranslator q) throws QueryException { if (count) { throw new AssertionFailure("count(*) must be handled differently"); } else if (avg) { int[] sqlTypes; try { sqlTypes = type.sqlTypes(q.factory); } catch (MappingException me) { throw new QueryException(me); } if (sqlTypes.length!=1) throw new QueryException("multi-column type in avg()"); int sqlType = sqlTypes[0]; if ( sqlType==Types.INTEGER || sqlType==Types.BIGINT || sqlType==Types.TINYINT ) { return Hibernate.FLOAT; } else { return type; } } // Begin change : Hugh Leather 10.07.2003 (See comment at top of file) else if (sum) { if (type instanceof BooleanType) { return Hibernate.INTEGER; } else { return type; } } // End change : Hugh Leather 10.07.2003 (See comment at top of file) else { return type; } } public void start(QueryTranslator q) { ready=true; first=true; aggregate=false; count = false; avg = false; afterNew = false; holderClass = null; } public void end(QueryTranslator q) { } } --------------------------------------------------------------------- JIRA INFORMATION: This message is automatically generated by JIRA. If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa If you want more information on JIRA, or have a bug to report see: http://www.atlassian.com/software/jira |